From 6e18bb6641b10b1b0bc420c9e3b71dadad6b988c Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Fri, 20 Mar 2026 08:35:18 +0100 Subject: [PATCH] Refresh the repo --- .gitignore | 44 +- README.md | 226 +- build.bat | 24 +- build/rtsgame/main.core | 249 - build/rtsgame/map.core | 213 - build_unix.sh | 4 +- {build/examples => examples}/_demo1.core | 586 +- .../any_and_variadic_args.core | 194 +- .../arrays_and_slices.core | 108 +- .../constant_expressions.core | 78 +- .../drawing_to_screen_using_windows_api.core | 150 +- .../examples => examples}/dynamic_typing.core | 108 +- .../language_basics.core | 206 +- .../operator_overloading.core | 50 +- .../order_independent_declarations.core | 148 +- examples/pathfind_visualizer | 1 + .../examples => examples}/polymorphism.core | 182 +- {build/examples => examples}/push_struct.core | 28 +- {build/examples => examples}/raymarcher.core | 420 +- .../runtime_type_information.core | 130 +- .../types_as_first_class_values.core | 96 +- {build/examples => examples}/unions.core | 80 +- .../using_multimedia.core | 22 +- info.bat | 2 - {build/modules => modules}/Arena.core | 98 +- {build/modules => modules}/Base.core | 248 +- {build/modules => modules}/GDI32.core | 80 +- {build/modules => modules}/KERNEL32.core | 258 +- {build/modules => modules}/Language.core | 118 +- {build/modules => modules}/LibC.core | 40 +- {build/modules => modules}/MathF32.core | 68 +- {build/modules => modules}/MathVec2.core | 64 +- {build/modules => modules}/MathVec3.core | 92 +- {build/modules => modules}/Multimedia.core | 128 +- {build/modules => modules}/OSWin32.core | 130 +- {build/modules => modules}/USER32.core | 398 +- {build/modules => modules}/WINMM.core | 10 +- {build/modules => modules}/array.core | 200 +- {build/modules => modules}/raylib.core | 2276 ++-- {build => modules}/vendor/raylib/raylib.h | 3176 +++--- {build => modules}/vendor/raylib/raymath.h | 4268 ++++---- {build => modules}/vendor/raylib/rlgl.h | 9480 ++++++++--------- .../modules => modules}/win32_multimedia.core | 708 +- src/base/array.hpp | 306 + base.cpp => src/base/base.cpp | 697 +- base_arena.cpp => src/base/base_arena.cpp | 287 +- .../base/base_data_structures.cpp | 906 +- base_string.cpp => src/base/base_string.cpp | 954 +- base_unicode.cpp => src/base/base_unicode.cpp | 548 +- src/base/defer.hpp | 21 + src/base/linked_list.h | 131 + stb_sprintf.h => src/base/stb_sprintf.h | 3850 +++---- c3_big_int.cpp => src/language/c3_big_int.cpp | 3810 +++---- c3_big_int.h => src/language/c3_big_int.h | 112 +- core_ast.cpp => src/language/core_ast.cpp | 1414 +-- .../language/core_codegen_c_language.cpp | 2142 ++-- .../language/core_compiler.cpp | 978 +- .../language/core_compiler.h | 466 +- .../language/core_compiler_includes.cpp | 66 +- .../language/core_compiler_interface.hpp | 1292 +-- .../language/core_generated.cpp | 164 +- .../language/core_globals.cpp | 14 +- .../language/core_lexing.cpp | 1576 +-- core_main.cpp => src/language/core_main.cpp | 351 +- .../language/core_parsing.cpp | 2218 ++-- .../language/core_polymorph.cpp | 1104 +- .../language/core_printer.cpp | 748 +- .../language/core_typechecking.cpp | 3916 +++---- .../language/core_typechecking.h | 142 +- core_types.cpp => src/language/core_types.cpp | 718 +- core_types.h => src/language/core_types.h | 90 +- os.h => src/os/os.h | 18 +- os_linux.cpp => src/os/os_linux.cpp | 390 +- os_windows.cpp => src/os/os_windows.cpp | 482 +- meta.bat => tools/meta.bat | 6 +- meta.py => tools/meta.py | 318 +- meta_run.py => tools/meta_run.py | 130 +- 77 files changed, 27788 insertions(+), 27766 deletions(-) delete mode 100644 build/rtsgame/main.core delete mode 100644 build/rtsgame/map.core rename {build/examples => examples}/_demo1.core (95%) rename {build/examples => examples}/any_and_variadic_args.core (95%) rename {build/examples => examples}/arrays_and_slices.core (96%) rename {build/examples => examples}/constant_expressions.core (96%) rename {build/examples => examples}/drawing_to_screen_using_windows_api.core (96%) rename {build/examples => examples}/dynamic_typing.core (96%) rename {build/examples => examples}/language_basics.core (96%) rename {build/examples => examples}/operator_overloading.core (96%) rename {build/examples => examples}/order_independent_declarations.core (96%) create mode 160000 examples/pathfind_visualizer rename {build/examples => examples}/polymorphism.core (94%) rename {build/examples => examples}/push_struct.core (96%) rename {build/examples => examples}/raymarcher.core (96%) rename {build/examples => examples}/runtime_type_information.core (96%) rename {build/examples => examples}/types_as_first_class_values.core (96%) rename {build/examples => examples}/unions.core (93%) rename {build/examples => examples}/using_multimedia.core (96%) delete mode 100644 info.bat rename {build/modules => modules}/Arena.core (96%) rename {build/modules => modules}/Base.core (97%) rename {build/modules => modules}/GDI32.core (98%) rename {build/modules => modules}/KERNEL32.core (97%) rename {build/modules => modules}/Language.core (93%) rename {build/modules => modules}/LibC.core (97%) rename {build/modules => modules}/MathF32.core (95%) rename {build/modules => modules}/MathVec2.core (98%) rename {build/modules => modules}/MathVec3.core (97%) rename {build/modules => modules}/Multimedia.core (95%) rename {build/modules => modules}/OSWin32.core (96%) rename {build/modules => modules}/USER32.core (97%) rename {build/modules => modules}/WINMM.core (96%) rename {build/modules => modules}/array.core (95%) rename {build/modules => modules}/raylib.core (98%) rename {build => modules}/vendor/raylib/raylib.h (98%) rename {build => modules}/vendor/raylib/raymath.h (96%) rename {build => modules}/vendor/raylib/rlgl.h (97%) rename {build/modules => modules}/win32_multimedia.core (96%) create mode 100644 src/base/array.hpp rename base.cpp => src/base/base.cpp (92%) rename base_arena.cpp => src/base/base_arena.cpp (52%) rename base_data_structures.cpp => src/base/base_data_structures.cpp (81%) rename base_string.cpp => src/base/base_string.cpp (96%) rename base_unicode.cpp => src/base/base_unicode.cpp (96%) create mode 100644 src/base/defer.hpp create mode 100644 src/base/linked_list.h rename stb_sprintf.h => src/base/stb_sprintf.h (97%) rename c3_big_int.cpp => src/language/c3_big_int.cpp (96%) rename c3_big_int.h => src/language/c3_big_int.h (98%) rename core_ast.cpp => src/language/core_ast.cpp (96%) rename core_codegen_c_language.cpp => src/language/core_codegen_c_language.cpp (96%) rename core_compiler.cpp => src/language/core_compiler.cpp (97%) rename core_compiler.h => src/language/core_compiler.h (96%) rename core_compiler_includes.cpp => src/language/core_compiler_includes.cpp (64%) rename core_compiler_interface.hpp => src/language/core_compiler_interface.hpp (95%) rename core_generated.cpp => src/language/core_generated.cpp (97%) rename core_globals.cpp => src/language/core_globals.cpp (96%) rename core_lexing.cpp => src/language/core_lexing.cpp (96%) rename core_main.cpp => src/language/core_main.cpp (88%) rename core_parsing.cpp => src/language/core_parsing.cpp (97%) rename core_polymorph.cpp => src/language/core_polymorph.cpp (97%) rename core_printer.cpp => src/language/core_printer.cpp (96%) rename core_typechecking.cpp => src/language/core_typechecking.cpp (97%) rename core_typechecking.h => src/language/core_typechecking.h (96%) rename core_types.cpp => src/language/core_types.cpp (97%) rename core_types.h => src/language/core_types.h (96%) rename os.h => src/os/os.h (94%) rename os_linux.cpp => src/os/os_linux.cpp (96%) rename os_windows.cpp => src/os/os_windows.cpp (96%) rename meta.bat => tools/meta.bat (96%) rename meta.py => tools/meta.py (95%) rename meta_run.py => tools/meta_run.py (96%) diff --git a/.gitignore b/.gitignore index c42359d..c99d292 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,23 @@ -*.exe -*.ilk -*.pdb -*.txt -*.4c -*.bin -*.rdbg -*.out -*.c -*.lib -*.o -*.10x -*.obj -*.exp -*.dll - -*.sublime-* -tests/ -backup* -start.bat -__pycache__ -build/examples/arms_race +*.exe +*.ilk +*.pdb +*.txt +*.4c +*.bin +*.rdbg +*.out +*.c +*.lib +*.o +*.10x +*.obj +*.exp +*.dll + +*.sublime-* +tests/ +backup* +start.bat +__pycache__ +build/examples/arms_race raylib.py \ No newline at end of file diff --git a/README.md b/README.md index 659e320..fb46265 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,43 @@ # The Core Language -A statically typed systems programming language that **you shouldn't use**. -I mark it as complete, it's pretty buggy but I had a lot of fun making it. -Learned a lot about language design and implementation, very worthwhile experience. -If you are intersted in programming languages, checkout bottom of the readme (there are useful resources there!). -The language is usable, -it lacks a macro system/generics for full convenience. It can be combined with C preprocessor -or python but where is fun in that? -Generics seem easy but I wanted something more powerful with compile time tree rewriting capabilities -but thats complicated, I dont have good design for that. :( +Core is an experimental systems programming language and compiler I built as one of my first "full" language projects. +It is intentionally kept here as a learning artifact: useful to read, fun to tinker with, and definitely not production-ready. -The basic premise of the language is simplicity and debuggability, -reinforcing already useful ideas from C,C++,Go,Odin,Jai and shaving off round edges. +## What this project is -## Simple drawing to window example +- A compiler written in C++ +- A language with `.core` source files +- A C code generator backend (Core -> C) +- A small standard-module set in `modules/` +- A collection of examples in `examples/` -``` odin +The compiler aims for simple syntax, explicit behavior, and debuggable output. + +## Current status + +- Archived hobby project / historical snapshot +- Works in many cases, but has known bugs and rough edges +- Best viewed as a language implementation study, not a stable toolchain +- I have made modifications over time and have not recently re-verified a clean build, so the project may not build out of the box + +If you are new to compilers, this repo can still be valuable as a real-world "first serious compiler" codebase. + +## Language highlights + +- Static typing with strict checks +- Untyped compile-time constants (with big-int support in constant evaluation) +- Order-independent declarations +- Modules via `#import` +- Conditional loading (for example OS-specific modules) +- Slices, arrays, structs, unions, enums +- Multiple return values +- Operator overloading +- Runtime type information (`Type`, `GetTypeInfo`, `Any`) + +## Tiny syntax taste + +```core #import "Multimedia.core" main :: (): int @@ -25,118 +46,111 @@ main :: (): int if Mu.key[Key.Escape].down Mu.quit = true - for y := 0, y < Mu.window.y, y+=1 - for x := 0, x < Mu.window.x, x+=1 - Mu.screen[x + y*Mu.window.x] = 0xFFFFFF00 + for y := 0, y < Mu.window.y, y += 1 + for x := 0, x < Mu.window.x, x += 1 + Mu.screen[x + y * Mu.window.x] = 0xFFFFFF00 + + return 0 ``` -## Features +For more examples, see: -* **Debuggers**(Visual Studio, Remedybg) **fully work** with the language, you can step through the program -* **No external dependencies**, you just setup clang and call build.bat -* Compiles to C code, in the future it will also compile to bytecode and hopefully a raw x64 executable -* Very strict Go like type system with untyped literals -* **Order independent declarations** -* Module system -* Tree shaking (unused code is not compiled) -* **Windows and Linux**(only tested on Ubuntu) support -* Conditional compilation -* Runtime type reflection -* Typesafe variadic arguments -* Operator overloading -* Slices -* Multiple return values +- `examples/language_basics.core` +- `examples/arrays_and_slices.core` +- `examples/operator_overloading.core` +- `examples/runtime_type_information.core` -## What's missing ? +## Compiler pipeline -- [x] Typechecking - very strict typesystem, inspired by Go, no implicit conversions outside of special types like void or Any. - - [x] Constant evaluation and constant folding - Folding and precomputing every expression that can be calculated at compile time. - - [x] Untyped types - Context dependent type assignment of constant expressions, this is a feature I really loved in Go, it makes constants work very well with a very strict type system and it makes errors like overflow of constants in C due to bad size specifier impossible - - [x] Infinite precision integers in constants using big ints! No more overflows or underflows (at least at compile time). +At a high level: -- [x] Module system - - [x] Lazy evaluation of modules (unused code is not compiled or typechecked) - - [x] Import module, load project file distinction - - [x] Linking to libraries through importing a module, module calls ```#link``` and that is added to linked libraries. +1. Lex + parse `.core` source into AST +2. Resolve symbols and types +3. Generate C code (`generated_main.c`) +4. Optionally compile generated C with `clang` -- [x] Order independent declarations - The ordering of functions in code files or modules does not matter, compiler figures all that stuff out for you. "main" can be wherever you want it to be and all functions should be available without problems +One design goal was debugger friendliness, so generated C includes line synchronization for stepping through source. -- [x] Synchronize generated C code with original source using line directives so that debuggers work - - [x] Fix overshoots when debugger goes to brace in c code +## Building the compiler -- [x] Expressions - - [x] Compounds with named fields and numbered fields - - [x] Functions calls with named arguments - - [x] All the standard binary, unary expressions - - [x] Pointer arithmetic and pointer as array - - [x] Dot access expression needs a redesign because it doesn't handle expressions after the dot, requires identifiers - - [ ] Casting might need a redesign not sure. From '->' to 'cast', and maybe more options like Odin ```transmute```. +### Windows -- [x] Runtime reflection - - [x] Package of type and pointer which enables dynamic typing - - [x] Types are values holding type ids, this way we can do if typeof(value) == S64 ;; blah - - [x] Typesafe variadic arguments using []Any slice (no more var args!) - - [x] Any semantics that make it easy to use (automatic unpacking to pointer and type) - - [x] Optional type information dump which allows an application to recursively serialize the data. Something you would need special tools for in C++. - - [ ] Is the design of this correct? That's a lot of data. +```bat +build.bat +``` -- [ ] Builtin data structures - - [x] Arrays - - [x] Slices - - [ ] Dynamic arrays. (Do we want this?) - - [ ] Hash tables. (Do we want this?) +This builds the compiler executable in `build/`. -- [x] Multiple return values +### Linux -- [ ] Polymorphism - - [x] C like void type - - [x] Dynamic typing using Any and operator overloading - - [ ] Generics aka compile time polymorphism aka parametric polymorphism - - [ ] Something akin to inheritence - - [ ] Something akin to C unions or Rust's enums +`build_unix.sh` exists, but the repo layout changed over time. If it fails, build manually: -- [x] Operator overloading - - [x] Binary operators - - [x] Unary operators - - [x] Bulletproof - - [ ] Assignment expressions? +```bash +clang src/language/core_main.cpp -O0 -Wall -Wno-unused-function -fno-exceptions -fdiagnostics-absolute-paths -g -o core.out +``` -- [x] Conditional compilation, you can include files based on a pattern: - - [x] "$os_multimedia.core" expands to "win32_multimedia.core" or "unix_multimedia.core" depending on the platform. +## Running it -- [x] Platforms - - [x] Conditional compilation - - [x] Windows - - [x] Fully working - - [x] Multimedia library - - [x] Linux (Only tested on ubuntu) - - [x] Paths - - [x] Reading files - - [x] Listing files - - [x] Virtual memory - - [ ] Multimedia library (maybe using SDL) +Compile one source file to C: -- [ ] Language constructs - - [x] Standard constructs like if, for loop etc. - - [ ] Jai like using statement - - [ ] Defer statement - - [ ] Unions (or something like unions) - - [ ] Unnamed blocks +```bash +./core.out examples/language_basics.core +``` -## Building +or on Windows: -1. Install **Visual Studio** and **Clang** -1. Run **build.bat** +```bat +build\main.exe examples\language_basics.core +``` -## Resources +This produces `generated_main.c`. -Stuff that helped me a lot programming the compiler. Hopefully they also will be helpful to you! +Then compile the generated C yourself (example): -* https://bitwise.handmade.network/ - series by Per Vognsen where he actually creates a C like language, very helpful, very hands on! -* https://hero.handmade.network/episode/code/day206/ - this episode of handmade hero started me on the entire compiler journey a long, long time ago. -* https://www.youtube.com/watch?v=TH9VCN6UkyQ&list=PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO - I have rewatched this playlist many this, searching for keywords and ideas. Jonathan Blow's compiler was a big inspiration of mine when learning programming languages. -* A Retargetable C Compiler: Design and Implementation by Christopher W. Fraser and David R. Hanson - sometimes looked at this as a reference to figure stuff out. Very helpful resource on compiler construction, the way it's written reads more like documentation but you use what you have. -* https://github.com/JoshuaManton/sif - looked at this as a reference from time to time, author seems like a Jonathan Blow fan so it was a good resource informed by similar resources as I used. -* https://github.com/c3lang/c3c - I sometimes looked at C3 compiler as a reference, the author also let me use his big int library he wrote sometime in the past! Thanks a lot! -* https://go.dev/blog/constants - article on golang type system, untyped types, constants that kind of stuff. -* https://github.com/gingerbill/odin - I sometimes peeked at the compiler to figure stuff out when I was confused about stuff. Also we get a free code indexing and syntax highlighting using odin sublime text plugin ! :D +```bash +clang generated_main.c -Wall -Wno-unused-function -Wno-parentheses-equality -g -o a.out +``` + +There is also a test mode: + +```bash +./core.out -testing +``` + +which iterates through `examples/` (and `tests/` if present), compiles, and runs them. + +## Repository map + +- `src/language/` - compiler front/mid/back-end implementation +- `src/base/` - memory, strings, containers, utilities +- `src/os/` - OS abstraction layer (Windows/Linux) +- `modules/` - language modules and platform bindings +- `examples/` - sample Core programs +- `tools/meta.py` - meta tables used to generate token/keyword boilerplate + +## Known rough edges + +- Project paths changed over time; some scripts may need adjustment +- Windows support is more mature than Linux in some areas +- No polished package manager or full modern tooling around the language +- Feature set is incomplete (for example, no finished generics system) + +## Why keep this repo public? + +Because complete-but-imperfect projects are useful. This codebase captures: + +- early language design decisions +- practical compiler architecture tradeoffs +- how a transpile-to-C strategy can speed up experimentation + +If you are building your own language, you may find useful ideas here (and just as many cautionary tales). + +## Learning resources that inspired this project + +- https://bitwise.handmade.network/ +- https://hero.handmade.network/episode/code/day206/ +- https://www.youtube.com/watch?v=TH9VCN6UkyQ&list=PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO +- A Retargetable C Compiler: Design and Implementation (Fraser/Hanson) +- https://github.com/c3lang/c3c +- https://go.dev/blog/constants +- https://github.com/gingerbill/odin diff --git a/build.bat b/build.bat index 3df2335..d4adfcc 100644 --- a/build.bat +++ b/build.bat @@ -1,10 +1,14 @@ -@echo off -call "..\misc\compile_setup.bat" - -bld --dont_compile_core -cd build -core_main.exe rtsgame/main.core -rem core_main.exe examples/unions.core -bld --dont_compile_core --link=vendor/raylib/windows/raylibdll.lib -rem build\generated_main.exe -cd .. +@echo off + +mkdir build +cd build + +cl ../src/language/core_main.cpp -Zi -nologo -W3 -wd4200 -wd4267 -wd4244 -diagnostics:column -Fe:main.exe user32.lib + +cd .. + +rem cd build\pathfind_visualizer + +rem call build.bat + +rem cd ..\.. diff --git a/build/rtsgame/main.core b/build/rtsgame/main.core deleted file mode 100644 index bc18b0f..0000000 --- a/build/rtsgame/main.core +++ /dev/null @@ -1,249 +0,0 @@ -#import "raylib.core" -#import "LibC.core" -A :: #import "array.core" -MAP :: #load "map.core" -sqrtf :: #foreign (value: F32): F32 - -V2I :: struct - x: int - y: int - -ANI_SetTile :: struct - set: bool - p: V2I - t: F32 - -WinX := 1280 -WinY := 720 -MouseX := 0 -MouseY := 0 -MouseP: Vector2 -Mode := 0 -RectX :: 16 -RectY :: 16 -Dt: F32 -ANI_SetTiles: A.Array(ANI_SetTile) - -MouseSelecting := false -MouseSelectionPivot: Vector2 -MouseSelectionBox: Rectangle -MouseSelectedActors: A.Array(*MAP.Actor) // @todo: ids - -main :: (): int - MAP.Init() - - // InitAudioDevice() - // sound := LoadSound("catune - Pass the town, and to the C.mp3") - // SetMasterVolume(0.01) - // PlaySound(sound) - - InitWindow(WinX, WinY, "Testing") - SetTargetFPS(60) - - orange := ORANGE - orange.a = 255/2 - - brown := BROWN - brown.a = 255/2 - - actor_color := DARKGREEN - actor_color.a = 255/2 - - past_actor_color := BLUE - past_actor_color.a = 255/2 - - target_color := RED - target_color.a = 255/2 - - COLOR_SelectionBox := GREEN - COLOR_SelectionBox.a = 255/2 - - COLOR_Selected := COLOR_SelectionBox - - testing := 0 - for !WindowShouldClose() - defer ;; testing += 1 - WinX = GetScreenWidth() - WinY = GetScreenHeight() - MouseX = GetMouseX() - MouseY = GetMouseY() - MouseP = GetMousePosition() - Dt = GetFrameTime() - map := &MAP.CurrentMap - - MouseSelecting = false - if IsMouseButtonDown(MOUSE_BUTTON_LEFT) - MouseSelecting = true - if IsMouseButtonPressed(MOUSE_BUTTON_LEFT) - MouseSelectionPivot = MouseP - MouseSelectionBox = { - MouseSelectionPivot.x, - MouseSelectionPivot.y, - MouseP.x - MouseSelectionPivot.x, - MouseP.y - MouseSelectionPivot.y - } - - if MouseSelectionBox.width < 0 - MouseSelectionBox.x += MouseSelectionBox.width - MouseSelectionBox.width = -MouseSelectionBox.width - if MouseSelectionBox.height < 0 - MouseSelectionBox.y += MouseSelectionBox.height - MouseSelectionBox.height = -MouseSelectionBox.height - - if IsKeyPressed(KEY_F1) - Mode = 0 - - if IsKeyPressed(KEY_F2) - Mode = 1 - - if IsKeyPressed(KEY_F3) - for i := 0, i < map.actors.len, i += 1 - it := map.actors.data + i - MAP.MoveTowardsTarget(it) - MAP.PathFindUpdate(map) - - if IsKeyPressed(KEY_F4) - MAP.RandomizeActors() - - - BeginDrawing() - ClearBackground(RAYWHITE) - - map_rectangle := Rectangle{0, 0, map.x->F32 * RectX, map.y->F32 * RectY} - DrawRectangleRec(map_rectangle, LIGHTGRAY) - - for x := 0, x < map.x, x += 1 - for y := 0, y < map.y, y += 1 - it := map.data + (x + y*map.x) - r := Rectangle{x->F32 * RectX, y->F32 * RectY, RectX, RectY} - r2 := Rectangle{r.x + 1, r.y + 1, r.width - 2, r.height - 2} - - colliding := CheckCollisionPointRec(MouseP, r) - color := RAYWHITE - if *it == 1 ;; color = GRAY - - - if Mode == 0 - if colliding && IsMouseButtonDown(MOUSE_BUTTON_LEFT) - A.Add(&ANI_SetTiles, {true, {x,y}}) - if colliding && IsMouseButtonDown(MOUSE_BUTTON_RIGHT) - A.Add(&ANI_SetTiles, {false, {x,y}}) - if colliding == true ;; color = {a = 100} - - DrawRectangleRec(r2, color) - - for tile_i := 0, tile_i < ANI_SetTiles.len, tile_i += 1 - tile_it := ANI_SetTiles.data + tile_i - remove := false - - t := tile_it.t - if tile_it.set == false - t = 1 - t - - - x := tile_it.p.x->F32 * RectX + 1 - y := tile_it.p.y->F32 * RectY + 1 - w: F32 = (RectX - 2) - h: F32 = (RectY - 2) - wt := w * t - ht := h * t - wd := w - wt - hd := h - ht - - r := Rectangle{x + wd/2, y + hd/2, wt, ht} - DrawRectangleRec(r, GRAY) - - - if tile_it.t > 1 - map_tile := map.data + (tile_it.p.x + tile_it.p.y*map.x) - if tile_it.set ;; *map_tile |= MAP.TILE_BLOCKER - else ;; *map_tile &= ~MAP.TILE_BLOCKER - remove = true - - tile_it.t += Dt*8 - if remove - A.UnorderedRemove(&ANI_SetTiles, tile_it) - tile_i -= 1 - - for i := 0, i < map.actors.len, i += 1 - actor_it := map.actors.data + i - target_r := MAP.Rect(actor_it.target_p) - - main_p := MAP.Circle(actor_it.p) - DrawCircleV(main_p, RectX/2, actor_color) - DrawRectangleRec(target_r, target_color) - - smaller_the_further: F32 = 0 - for tile_i := actor_it.tiles_visited.len - 1, tile_i >= 0, tile_i -= 1 - tile_it := actor_it.tiles_visited.data + tile_i - p := MAP.Circle({tile_it.x, tile_it.y}) - DrawCircleV(p, RectX/2 - smaller_the_further, past_actor_color) - smaller_the_further += 0.5 - - for path_i := 0, path_i < actor_it.open_paths.len, path_i += 1 - path_it := actor_it.open_paths.data + path_i - path_r := MAP.Rect(path_it.p) - DrawRectangleRec(path_r, orange) - s := TextFormat("%d", sqrtf(path_it.value_to_sort_by->F32)->int) - DrawText(s, path_r.x->int, path_r.y->int, 1, RAYWHITE) - - for path_i := 0, path_i < actor_it.close_paths.len, path_i += 1 - path_it := actor_it.close_paths.data + path_i - path_r := MAP.Rect(path_it.p) - DrawRectangleRec(path_r, brown) - - for path_i := 0, path_i < actor_it.history.len, path_i += 1 - path_it := actor_it.history.data + path_i - - p0 := MAP.Circle(path_it.came_from) - p1 := MAP.Circle(path_it.p) - - DrawLineEx(p0, p1, 5, LIGHTGRAY) - DrawCircleV(p0, 4, LIGHTGRAY) - DrawCircleV(p1, 4, LIGHTGRAY) - - if Mode == 1 - for actor_i := 0, actor_i < MouseSelectedActors.len, actor_i += 1 - actor_it := MouseSelectedActors.data[actor_i] - actor_box := MAP.Rect(actor_it.p) - DrawRectangleRec(actor_box, COLOR_Selected) - - if IsMouseButtonPressed(MOUSE_BUTTON_RIGHT) - p := MAP.ScreenToMap(MouseP) - MAP.SetTargetP(actor_it, p) - - if MouseSelecting - A.Reset(&MouseSelectedActors) - - for actor_i := 0, actor_i < map.actors.len, actor_i += 1 - actor_it := map.actors.data + actor_i - actor_box := MAP.Rect(actor_it.p) - - if CheckCollisionRecs(actor_box, MouseSelectionBox) - A.Add(&MouseSelectedActors, actor_it) - - DrawRectangleRec(MouseSelectionBox, COLOR_SelectionBox) - - - menu_open := false - if menu_open - text_size := 24 - text_p := 4 - text_y := WinY - text_size - - DrawText("Space :: PathFind", text_p, text_y, text_size, GRAY) - text_y -= text_size - DrawText("F4 :: Randomize actors", text_p, text_y, text_size, GRAY) - text_y -= text_size - DrawText("F3 :: Simulate actors", text_p, text_y, text_size, GRAY) - text_y -= text_size - text: *char = "Mode(F1) :: Block placing" - if Mode == 1 ;; text = "Mode(F2) :: Actor placing" - DrawText(text, text_p, text_y, text_size, GRAY) - text_y -= text_size - - - - EndDrawing() - - return 0 diff --git a/build/rtsgame/map.core b/build/rtsgame/map.core deleted file mode 100644 index faea5e1..0000000 --- a/build/rtsgame/map.core +++ /dev/null @@ -1,213 +0,0 @@ -CurrentMap: Map - -PATH_SEARCHING :: 0 -PATH_REACHED :: 1 -PATH_UNREACHABLE :: 2 - -TILE_BLOCKER :: 1 -TILE_ACTOR_IS_STANDING :: 2 - -Tile :: int -Map :: struct - data: *Tile - x: int - y: int - actors: A.Array(Actor) - -Actor :: struct - p: V2I - target_p: V2I - map: *Map - - open_paths: A.Array(Path) - close_paths: A.Array(Path) - tiles_visited: A.Array(V2I) - history: A.Array(Path) - -Path :: struct - value_to_sort_by: int // distance from target - p: V2I - came_from: V2I - -Rect :: (p: V2I): Rectangle - result := Rectangle{p.x->F32 * RectX, p.y->F32 * RectY, RectX, RectY} - return result - -Circle :: (p: V2I): Vector2 - result := Vector2{p.x->F32 * RectX + RectX/2, p.y->F32 * RectY + RectY/2} - return result - -ScreenToMap :: (p: Vector2): V2I - p0 := p.x / RectX - p1 := p.y / RectY - result := V2I{p0->int, p1->int} - return result - -AddActor :: (map: *Map, p: V2I): *Actor - A.Add(&map.actors, {p, p, map}) - Assert(map.data[p.x + p.y * map.x] == 0) - map.data[p.x + p.y * map.x] |= TILE_ACTOR_IS_STANDING - actor := A.GetLast(&CurrentMap.actors) - return actor - -ActorSetP :: (actor: *Actor, p: V2I) - map := actor.map - new_tile := map.data + p.x + p.y * map.x - if *new_tile != 0 ;; return - - tile := map.data + actor.p.x + actor.p.y * map.x - Assert((*tile & TILE_ACTOR_IS_STANDING) != 0) - *tile &= ~TILE_ACTOR_IS_STANDING - - *new_tile |= TILE_ACTOR_IS_STANDING - actor.p = p - - A.Reset(&actor.tiles_visited) - A.Reset(&actor.history) - A.Reset(&actor.open_paths) - A.Reset(&actor.close_paths) - -SetTargetP :: (s: *Actor, p: V2I) - s.target_p = p - A.Reset(&s.tiles_visited) - A.Reset(&s.history) - A.Reset(&s.open_paths) - A.Reset(&s.close_paths) - -GetRandomP :: (m: *Map): V2I - result: V2I = {GetRandomValue(0, CurrentMap.x - 1), GetRandomValue(0, CurrentMap.y - 1)} - return result - -GetRandomUnblockedP :: (m: *Map): V2I - for i := 0, i < 128, i += 1 - p := GetRandomP(m) - if m.data[p.x + p.y * m.x] == 0 - return p - - Assert(false, "Invalid codepath") - return {} - -Init :: () - CurrentMap.x = WinX / RectX - CurrentMap.y = WinY / RectY - bytes := sizeof(Tile) * CurrentMap.x->U64 * CurrentMap.y->U64 - CurrentMap.data = malloc(bytes) - memset(CurrentMap.data, 0, bytes) - - actor := AddActor(&CurrentMap, GetRandomUnblockedP(&CurrentMap)) - actor.target_p = GetRandomUnblockedP(&CurrentMap) - - actor2 := AddActor(&CurrentMap, GetRandomUnblockedP(&CurrentMap)) - actor2.target_p = GetRandomUnblockedP(&CurrentMap) - -RandomizeActors :: () - map := &CurrentMap - for i := 0, i < map.actors.len, i += 1 - it := map.actors.data + i - p := GetRandomUnblockedP(&CurrentMap) - ActorSetP(it, p) - it.target_p = GetRandomUnblockedP(&CurrentMap) - -InsertOpenPath :: (s: *Actor, p: V2I, came_from: V2I, ignore_blocks: bool = false) - if p.x < 0 || p.x >= s.map.x ;; return - if p.y < 0 || p.y >= s.map.y ;; return - if ignore_blocks == false && s.map.data[p.x + p.y * s.map.x] != 0 ;; return - - for i := 0, i < s.close_paths.len, i += 1 - it := s.close_paths.data + i - if it.p.x == p.x && it.p.y == p.y ;; return - - for i := 0, i < s.open_paths.len, i += 1 - it := s.open_paths.data + i - if it.p.x == p.x && it.p.y == p.y ;; return - - dx := s.target_p.x - p.x - dy := s.target_p.y - p.y - d := dx*dx + dy*dy - A.InsertSortedDecreasing(&s.open_paths, {d, p, came_from}) - -GetCloseP :: (s: *Actor, p: V2I): *Path - for i := 0, i < s.close_paths.len, i += 1 - it := s.close_paths.data + i - if it.p.x == p.x && it.p.y == p.y ;; return it - - Assert(false, "Invalid codepath") - return 0 - -RecomputeHistory :: (s: *Actor) - if s.close_paths.len > 1 - A.Reset(&s.history) - it := A.GetLast(&s.close_paths) - A.Add(&s.history, *it) - for i := 0,,i += 1 - if it.p.x == s.p.x && it.p.y == s.p.y ;; break - if i > 512 - A.Reset(&s.history) - break - it = GetCloseP(s, it.came_from) - A.Add(&s.history, *it) - A.Pop(&s.history) - -MoveTowardsTarget :: (s: *Actor) - tile := s.map.data + s.p.x + s.p.y * s.map.x - if s.history.len > 0 - step := A.Pop(&s.history) - new_tile := s.map.data + step.p.x + step.p.y * s.map.x - if *new_tile == 0 - A.Add(&s.tiles_visited, s.p) - s.p = step.p - *tile &= ~TILE_ACTOR_IS_STANDING - *new_tile |= TILE_ACTOR_IS_STANDING - -PathFindUpdate :: (map: *Map) - for actor_i := 0, actor_i < map.actors.len, actor_i += 1 - s := map.actors.data + actor_i - for i := 0, i < s.history.len, i += 1 - it := s.history.data + i - - tile := s.map.data[it.p.x + it.p.y * s.map.x] - if tile != 0 - A.Reset(&s.open_paths) - A.Reset(&s.close_paths) - A.Reset(&s.history) - break - - PathFind(s) - -PathFindStep :: (s: *Actor, compute_history: bool = true): bool - if s.open_paths.len == 0 - // Reset if we didnt find solution - if s.close_paths.len != 0 - last := A.GetLast(&s.close_paths) - reached_target := last.p.x == s.target_p.x && last.p.y == s.target_p.y - if reached_target == false - A.Reset(&s.open_paths) - A.Reset(&s.close_paths) - A.Reset(&s.history) - - InsertOpenPath(s, s.p, s.p, ignore_blocks = true) - - if s.close_paths.len != 0 - last := A.GetLast(&s.close_paths) - reached_target := last.p.x == s.target_p.x && last.p.y == s.target_p.y - if reached_target - return true - - it := A.Pop(&s.open_paths) - A.Add(&s.close_paths, it) - - - for y := -1, y <= 1, y += 1 - for x := -1, x <= 1, x += 1 - if x == 0 && y == 0 ;; continue - p := V2I{it.p.x + x, it.p.y + y} - InsertOpenPath(s, p, it.p) - - if compute_history ;; RecomputeHistory(s) - return false - -PathFind :: (s: *Actor) - for i := 0, i < 32, i += 1 - done := PathFindStep(s, false) - if done ;; break - RecomputeHistory(s) diff --git a/build_unix.sh b/build_unix.sh index ad25681..d7c0849 100644 --- a/build_unix.sh +++ b/build_unix.sh @@ -1,3 +1,3 @@ -#!/bin/bash - +#!/bin/bash + clang core_main.cpp -O0 -Wall -Wno-unused-function -fno-exceptions -fdiagnostics-absolute-paths -g -o core.out \ No newline at end of file diff --git a/build/examples/_demo1.core b/examples/_demo1.core similarity index 95% rename from build/examples/_demo1.core rename to examples/_demo1.core index fd88696..44da132 100644 --- a/build/examples/_demo1.core +++ b/examples/_demo1.core @@ -1,293 +1,293 @@ -/* EXTRA CREDITS (bottom of README.md on github) - -* https://bitwise.handmade.network/ - series by Per Vognsen where he actually creates a C like language, very helpful, very hands on! -* https://hero.handmade.network/episode/code/day206/ - this episode of handmade hero started me on the entire compiler journey a long, long time ago. -* https://www.youtube.com/watch?v=TH9VCN6UkyQ&list=PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO - I have rewatched this playlist many this, searching for keywords and ideas. Jonathan Blow's compiler was a big inspiration of mine when learning programming languages. -* A Retargetable C Compiler: Design and Implementation by Christopher W. Fraser and David R. Hanson - sometimes looked at this as a reference to figure stuff out. Very helpful resource on compiler construction, the way it's written reads more like documentation but you use what you have. -* https://github.com/JoshuaManton/sif - looked at this as a reference from time to time, author seems like a Jonathan Blow fan so it was a good resource informed by similar resources as I used. -* https://github.com/c3lang/c3c - I sometimes looked at C3 compiler as a reference, the author also let me use his big int library he wrote sometime in the past! Thanks a lot! -* https://go.dev/blog/constants - article on golang type system, untyped types, constants that kind of stuff. -* https://github.com/gingerbill/odin - I sometimes peeked at the compiler to figure stuff out when I was confused about stuff. Also we get a free code indexing and syntax highlighting using odin sublime text plugin ! :D - -*/ - - -// -// #1 -// -// * Inspired by: Per Vognsen(Ion), Jonathan Blow(Jai) -// * Standard programming constructs -// * No forward declarations needed -// * Named function arguments -// * Enum values are nicely namespaced -#import "Multimedia.core" - -main :: (): int - StartMultimedia(title = "Hello people!") - DrawYellowFill() - DrawCoolGradient() - DrawCircleWithGradient() - AnimateTransmorphingCircle() - RaymarchSphere() - -DrawYellowFill :: () - for y := 0, y < Mu.window.y, y+=1 - for x := 0, x < Mu.window.x, x+=1 - Mu.screen[x + y*Mu.window.x] = 0xFFFFFF00 - - for UpdateMultimedia() - if Mu.key[Key.Escape].down - Mu.key[Key.Escape].down = false - break - - - - - - - - - - - - - - - - -// -// #2 -// -// * Modules -// * User names the imports -// * Aliasing -// * Operator overloads -F :: #import "MathF32.core" -V3 :: #import "MathVec3.core" -V2 :: #import "MathVec2.core" -Vec3 :: V3.Vec3 - -DrawCoolGradient :: () - for y := 0, y < Mu.window.y, y+=1 - for x := 0, x < Mu.window.x, x+=1 - - // Normalize to range {0, 1} - uv := V2.Vec2{x->F32, y->F32} / Mu.window.sizef - - // Move to range {-1, 1} - uv = uv*2->F32 - 1->F32 - Assert(uv.x >= -1 && uv.x <= 1) - Assert(uv.y >= -1 && uv.y <= 1) - - color := Vec3{uv.x, uv.y, 1} - Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color) - - for UpdateMultimedia() - if Mu.key[Key.Escape].down - Mu.key[Key.Escape].down = false - break - - - - - - - - - - - - - - - - - -// -// #3 -// -// * The ';;' operator -// * The ';' operator -// * Compound expressions, named parameters! -DrawCircleWithGradient :: () - default_color := V3.ConvertToARGB({x = 1, y = 1, z = 1}) - ratio := Mu.window.sizef.x / Mu.window.sizef.y - for y := 0, y < Mu.window.y, y+=1 - for x := 0, x < Mu.window.x, x+=1 - - // Normalize to range {0, 1} - uv := V2.Vec2{x->F32, y->F32} / Mu.window.sizef - - // Move to range {-1, 1} - uv = uv*2->F32 - 1->F32 - uv.x *= ratio - - if uv.x*uv.x + uv.y*uv.y < 0.5 - color := Vec3{uv.x, uv.y, 1} - Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color) - else;; Mu.screen[x + y*Mu.window.x] = default_color - - for UpdateMultimedia() - if Mu.key[Key.Escape].down - Mu.key[Key.Escape].down = false - break - - - - - - - - - - - - - -// -// #4 -// -AnimateTransmorphingCircle :: () - for UpdateMultimedia() - if Mu.key[Key.Escape].down - Mu.key[Key.Escape].down = false - break - - default_color := V3.ConvertToARGB({x = 1, y = 1, z = 1}) - ratio := Mu.window.sizef.x / Mu.window.sizef.y - for y := 0, y < Mu.window.y, y+=1 - for x := 0, x < Mu.window.x, x+=1 - - // Normalize to range {0, 1} - uv := V2.Vec2{x->F32, y->F32} / Mu.window.sizef - - // Move to range {-1, 1} - uv = uv*2->F32 - 1->F32 - uv.x *= ratio - - if F.Cos(Mu.time.total->F32)*uv.x*uv.x + F.Sin(Mu.time.total->F32)*uv.y*uv.y < 0.5 - color := Vec3{uv.x, uv.y, 1} - Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color) - else ;; Mu.screen[x + y*Mu.window.x] = default_color - - - - - - - - - - - - - - - - - - -// -// #5 -// -Epsilon :: 0.00001 - -SphereSDF :: (pos: Vec3): F32 - result := V3.Length(pos) - 1.0 - return result - -RaymarchSphere :: () - for UpdateMultimedia() - if Mu.key[Key.Escape].down - Mu.key[Key.Escape].down = false - break - - up := Vec3{0, 1, 0} - forward := Vec3{0, 0, -1} - side := V3.Normalize(V3.Cross(forward, up)) - - LightPos := Vec3{2,4,2} - LightPos.x = F.Cos(Mu.time.total->F32)*4 - LightPos.y = F.Sin(Mu.time.total->F32)*4 - - ambient_color := Vec3{0.2,0.2,0.2} - diffuse_color := Vec3{0.7,0.2,0.2} - eye := Vec3{0, 0, 2} - light_intensity :: 1.2->F32 - - Xf := 1 / Mu.window.sizef.x - Yf := 1 / Mu.window.sizef.y - ratio := Mu.window.sizef.x / Mu.window.sizef.y - for y := 0, y < Mu.window.y, y+=1 - for x := 0, x < Mu.window.x, x+=1 - uv := Vec3{x->F32 * Xf * 2 - 1, y->F32 * Yf * 2 - 1, 1.0} - uv.x *= ratio - dir := V3.Normalize(Vec3{V3.Dot(side, uv), V3.Dot(up, uv), V3.Dot(forward, uv)}) - - t: F32 - end: F32 = 100.0 - hit := true - p: Vec3 - for i := 0, i < 255, i+=1 - p = eye + dir*t - - distance := SphereSDF(p) - if distance < Epsilon - break - - t += distance - if distance >= end - hit = false - break - - if hit - normal := V3.Normalize(Vec3{ - SphereSDF({p.x + Epsilon, p.y, p.z}) - SphereSDF({p.x - Epsilon, p.y, p.z}), - SphereSDF({p.x, p.y + Epsilon, p.z}) - SphereSDF({p.x, p.y - Epsilon, p.z}), - SphereSDF({p.x, p.y, p.z + Epsilon}) - SphereSDF({p.x, p.y, p.z - Epsilon}), - }) - - light_to_point := V3.Normalize(LightPos - p) - - ambient :: 0.2->F32 - diffuse := V3.Dot(normal, light_to_point) - - color := ambient_color*ambient->F32 - if diffuse > Epsilon - color = color + diffuse_color*diffuse - color = color * light_intensity - - // Gamma correction - color.x = F.SquareRoot(color.x) - color.y = F.SquareRoot(color.y) - color.z = F.SquareRoot(color.z) - Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color) - - else;; Mu.screen[x + y*Mu.window.x] = 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - +/* EXTRA CREDITS (bottom of README.md on github) + +* https://bitwise.handmade.network/ - series by Per Vognsen where he actually creates a C like language, very helpful, very hands on! +* https://hero.handmade.network/episode/code/day206/ - this episode of handmade hero started me on the entire compiler journey a long, long time ago. +* https://www.youtube.com/watch?v=TH9VCN6UkyQ&list=PLmV5I2fxaiCKfxMBrNsU1kgKJXD3PkyxO - I have rewatched this playlist many this, searching for keywords and ideas. Jonathan Blow's compiler was a big inspiration of mine when learning programming languages. +* A Retargetable C Compiler: Design and Implementation by Christopher W. Fraser and David R. Hanson - sometimes looked at this as a reference to figure stuff out. Very helpful resource on compiler construction, the way it's written reads more like documentation but you use what you have. +* https://github.com/JoshuaManton/sif - looked at this as a reference from time to time, author seems like a Jonathan Blow fan so it was a good resource informed by similar resources as I used. +* https://github.com/c3lang/c3c - I sometimes looked at C3 compiler as a reference, the author also let me use his big int library he wrote sometime in the past! Thanks a lot! +* https://go.dev/blog/constants - article on golang type system, untyped types, constants that kind of stuff. +* https://github.com/gingerbill/odin - I sometimes peeked at the compiler to figure stuff out when I was confused about stuff. Also we get a free code indexing and syntax highlighting using odin sublime text plugin ! :D + +*/ + + +// +// #1 +// +// * Inspired by: Per Vognsen(Ion), Jonathan Blow(Jai) +// * Standard programming constructs +// * No forward declarations needed +// * Named function arguments +// * Enum values are nicely namespaced +#import "Multimedia.core" + +main :: (): int + StartMultimedia(title = "Hello people!") + DrawYellowFill() + DrawCoolGradient() + DrawCircleWithGradient() + AnimateTransmorphingCircle() + RaymarchSphere() + +DrawYellowFill :: () + for y := 0, y < Mu.window.y, y+=1 + for x := 0, x < Mu.window.x, x+=1 + Mu.screen[x + y*Mu.window.x] = 0xFFFFFF00 + + for UpdateMultimedia() + if Mu.key[Key.Escape].down + Mu.key[Key.Escape].down = false + break + + + + + + + + + + + + + + + + +// +// #2 +// +// * Modules +// * User names the imports +// * Aliasing +// * Operator overloads +F :: #import "MathF32.core" +V3 :: #import "MathVec3.core" +V2 :: #import "MathVec2.core" +Vec3 :: V3.Vec3 + +DrawCoolGradient :: () + for y := 0, y < Mu.window.y, y+=1 + for x := 0, x < Mu.window.x, x+=1 + + // Normalize to range {0, 1} + uv := V2.Vec2{x->F32, y->F32} / Mu.window.sizef + + // Move to range {-1, 1} + uv = uv*2->F32 - 1->F32 + Assert(uv.x >= -1 && uv.x <= 1) + Assert(uv.y >= -1 && uv.y <= 1) + + color := Vec3{uv.x, uv.y, 1} + Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color) + + for UpdateMultimedia() + if Mu.key[Key.Escape].down + Mu.key[Key.Escape].down = false + break + + + + + + + + + + + + + + + + + +// +// #3 +// +// * The ';;' operator +// * The ';' operator +// * Compound expressions, named parameters! +DrawCircleWithGradient :: () + default_color := V3.ConvertToARGB({x = 1, y = 1, z = 1}) + ratio := Mu.window.sizef.x / Mu.window.sizef.y + for y := 0, y < Mu.window.y, y+=1 + for x := 0, x < Mu.window.x, x+=1 + + // Normalize to range {0, 1} + uv := V2.Vec2{x->F32, y->F32} / Mu.window.sizef + + // Move to range {-1, 1} + uv = uv*2->F32 - 1->F32 + uv.x *= ratio + + if uv.x*uv.x + uv.y*uv.y < 0.5 + color := Vec3{uv.x, uv.y, 1} + Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color) + else;; Mu.screen[x + y*Mu.window.x] = default_color + + for UpdateMultimedia() + if Mu.key[Key.Escape].down + Mu.key[Key.Escape].down = false + break + + + + + + + + + + + + + +// +// #4 +// +AnimateTransmorphingCircle :: () + for UpdateMultimedia() + if Mu.key[Key.Escape].down + Mu.key[Key.Escape].down = false + break + + default_color := V3.ConvertToARGB({x = 1, y = 1, z = 1}) + ratio := Mu.window.sizef.x / Mu.window.sizef.y + for y := 0, y < Mu.window.y, y+=1 + for x := 0, x < Mu.window.x, x+=1 + + // Normalize to range {0, 1} + uv := V2.Vec2{x->F32, y->F32} / Mu.window.sizef + + // Move to range {-1, 1} + uv = uv*2->F32 - 1->F32 + uv.x *= ratio + + if F.Cos(Mu.time.total->F32)*uv.x*uv.x + F.Sin(Mu.time.total->F32)*uv.y*uv.y < 0.5 + color := Vec3{uv.x, uv.y, 1} + Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color) + else ;; Mu.screen[x + y*Mu.window.x] = default_color + + + + + + + + + + + + + + + + + + +// +// #5 +// +Epsilon :: 0.00001 + +SphereSDF :: (pos: Vec3): F32 + result := V3.Length(pos) - 1.0 + return result + +RaymarchSphere :: () + for UpdateMultimedia() + if Mu.key[Key.Escape].down + Mu.key[Key.Escape].down = false + break + + up := Vec3{0, 1, 0} + forward := Vec3{0, 0, -1} + side := V3.Normalize(V3.Cross(forward, up)) + + LightPos := Vec3{2,4,2} + LightPos.x = F.Cos(Mu.time.total->F32)*4 + LightPos.y = F.Sin(Mu.time.total->F32)*4 + + ambient_color := Vec3{0.2,0.2,0.2} + diffuse_color := Vec3{0.7,0.2,0.2} + eye := Vec3{0, 0, 2} + light_intensity :: 1.2->F32 + + Xf := 1 / Mu.window.sizef.x + Yf := 1 / Mu.window.sizef.y + ratio := Mu.window.sizef.x / Mu.window.sizef.y + for y := 0, y < Mu.window.y, y+=1 + for x := 0, x < Mu.window.x, x+=1 + uv := Vec3{x->F32 * Xf * 2 - 1, y->F32 * Yf * 2 - 1, 1.0} + uv.x *= ratio + dir := V3.Normalize(Vec3{V3.Dot(side, uv), V3.Dot(up, uv), V3.Dot(forward, uv)}) + + t: F32 + end: F32 = 100.0 + hit := true + p: Vec3 + for i := 0, i < 255, i+=1 + p = eye + dir*t + + distance := SphereSDF(p) + if distance < Epsilon + break + + t += distance + if distance >= end + hit = false + break + + if hit + normal := V3.Normalize(Vec3{ + SphereSDF({p.x + Epsilon, p.y, p.z}) - SphereSDF({p.x - Epsilon, p.y, p.z}), + SphereSDF({p.x, p.y + Epsilon, p.z}) - SphereSDF({p.x, p.y - Epsilon, p.z}), + SphereSDF({p.x, p.y, p.z + Epsilon}) - SphereSDF({p.x, p.y, p.z - Epsilon}), + }) + + light_to_point := V3.Normalize(LightPos - p) + + ambient :: 0.2->F32 + diffuse := V3.Dot(normal, light_to_point) + + color := ambient_color*ambient->F32 + if diffuse > Epsilon + color = color + diffuse_color*diffuse + color = color * light_intensity + + // Gamma correction + color.x = F.SquareRoot(color.x) + color.y = F.SquareRoot(color.y) + color.z = F.SquareRoot(color.z) + Mu.screen[x + y*Mu.window.x] = V3.ConvertToARGB(color) + + else;; Mu.screen[x + y*Mu.window.x] = 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/examples/any_and_variadic_args.core b/examples/any_and_variadic_args.core similarity index 95% rename from build/examples/any_and_variadic_args.core rename to examples/any_and_variadic_args.core index f54888c..9c47770 100644 --- a/build/examples/any_and_variadic_args.core +++ b/examples/any_and_variadic_args.core @@ -1,98 +1,98 @@ - -VariadicArguments :: (string: *char, args: []Any): Any - return args[0] - -AnyArguments :: (values: []Any) - for values - Assert(it.type == int) - Assert(*(values[0].data->*int) == 10) - Assert(*(values[1].data->*int) == 20) - -/** - * C++ version 0.4 char* style "itoa": - * Written by Lukás Chmela - * Released under GPLv3. - */ -IntegerToString :: (value: int, result: *U8, base: int): *U8 - // check that the base if valid - if (base < 2) || (base > 36) - *result = 0 // ' - return result - - ptr := result - ptr1 := result - tmp_char: U8 - tmp_value: int - str := "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" - - for value != 0 - tmp_value = value - value /= base - *ptr++ = str[35 + (tmp_value - value * base)] - - // Apply negative sign - if tmp_value < 0 - *ptr++ = '-' - *ptr-- = 0 - for ptr1 < ptr - tmp_char = *ptr - *ptr-- = *ptr1 - *ptr1++ = tmp_char - return result - -StringToDouble :: (s: String): F64 - sign: F64 = 1.0 - i := 0 - - if s[i] == '-' - sign = -1 - i += 1 - elif s[i] == '+' - i += 1 - - for , i < Len(s), i+= 1 - pass // BACKUP - - return 0 - -FormatString :: (buffer: *U8, buffer_len: U64, string: String, args: []Any) - // @todo(krzosa): Add consideration of buffer SIZE! Add some function to handle this OutStr or something - arg_counter := 0 - out_buffer_len := 0 - for i := 0, i < Len(string), i+=1 - if string[i] == '%' - Assert(arg_counter < Len(args), "Passed too many [%] to the string formating function") - arg := args[arg_counter++] - - if arg.type == int - value := *(arg.data->*int) - itoa_buff: [64]U8 - p := IntegerToString(value, &itoa_buff[0], 10) - - for *p != 0 - buffer[out_buffer_len++] = *p++ - else;; Assert(false) - else - buffer[out_buffer_len++] = string[i] - - -main :: (): int - a := 10 - b := 20 - values := []Any{a, b} - - for values - Assert(it.type == int) - AnyArguments({a,b}) - c := VariadicArguments("Test", args = {a+b,b}) - Assert(*(c.data->*int) == 30) - d := VariadicArguments("Test", {b,a}) - Assert(*(d.data->*int) == b) - - Assert(*(values[0].data->*int) == 10) - Assert(*(values[1].data->*int) == 20) - - buf: [128]U8 - FormatString(&buf[0], Len(buf), "Test % %", {32, 156}) - + +VariadicArguments :: (string: *char, args: []Any): Any + return args[0] + +AnyArguments :: (values: []Any) + for values + Assert(it.type == int) + Assert(*(values[0].data->*int) == 10) + Assert(*(values[1].data->*int) == 20) + +/** + * C++ version 0.4 char* style "itoa": + * Written by Lukás Chmela + * Released under GPLv3. + */ +IntegerToString :: (value: int, result: *U8, base: int): *U8 + // check that the base if valid + if (base < 2) || (base > 36) + *result = 0 // ' + return result + + ptr := result + ptr1 := result + tmp_char: U8 + tmp_value: int + str := "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" + + for value != 0 + tmp_value = value + value /= base + *ptr++ = str[35 + (tmp_value - value * base)] + + // Apply negative sign + if tmp_value < 0 + *ptr++ = '-' + *ptr-- = 0 + for ptr1 < ptr + tmp_char = *ptr + *ptr-- = *ptr1 + *ptr1++ = tmp_char + return result + +StringToDouble :: (s: String): F64 + sign: F64 = 1.0 + i := 0 + + if s[i] == '-' + sign = -1 + i += 1 + elif s[i] == '+' + i += 1 + + for , i < Len(s), i+= 1 + pass // BACKUP + + return 0 + +FormatString :: (buffer: *U8, buffer_len: U64, string: String, args: []Any) + // @todo(krzosa): Add consideration of buffer SIZE! Add some function to handle this OutStr or something + arg_counter := 0 + out_buffer_len := 0 + for i := 0, i < Len(string), i+=1 + if string[i] == '%' + Assert(arg_counter < Len(args), "Passed too many [%] to the string formating function") + arg := args[arg_counter++] + + if arg.type == int + value := *(arg.data->*int) + itoa_buff: [64]U8 + p := IntegerToString(value, &itoa_buff[0], 10) + + for *p != 0 + buffer[out_buffer_len++] = *p++ + else;; Assert(false) + else + buffer[out_buffer_len++] = string[i] + + +main :: (): int + a := 10 + b := 20 + values := []Any{a, b} + + for values + Assert(it.type == int) + AnyArguments({a,b}) + c := VariadicArguments("Test", args = {a+b,b}) + Assert(*(c.data->*int) == 30) + d := VariadicArguments("Test", {b,a}) + Assert(*(d.data->*int) == b) + + Assert(*(values[0].data->*int) == 10) + Assert(*(values[1].data->*int) == 20) + + buf: [128]U8 + FormatString(&buf[0], Len(buf), "Test % %", {32, 156}) + return 0 \ No newline at end of file diff --git a/build/examples/arrays_and_slices.core b/examples/arrays_and_slices.core similarity index 96% rename from build/examples/arrays_and_slices.core rename to examples/arrays_and_slices.core index ca2da39..5dee0cb 100644 --- a/build/examples/arrays_and_slices.core +++ b/examples/arrays_and_slices.core @@ -1,55 +1,55 @@ -/* -Static arrays are exactly like c arrays, difference is -we can easily then get a slice of that array. - -Slices are static arrays + length, they simplify array handling. -This allows us to pass an array into a function easily, then that function -can still access that length information easily. - -Passing a pointer to array + length is a common pattern in C. So it would -be nice if handling of that was simplified. -*/ - - - -main :: (): int - static_array: [8]int - - // We can get size of array using Length builtin - #Assert(Len(static_array) == 8) - - // Accessing values is like in C - // Variables are zeroed by default - Assert(static_array[1] == 0) - - element2 := static_array[2] - element0: int = static_array[0] - Assert(element0 == 0 && element2 == 0) - - // We can loop through arrays - // this implicitly defines 'it' variable - for static_array - *it = 1 - - // We set all variables to 1 so - Assert(static_array[6] == 1) - - // This is how slice is defined, no [] index in between brackets - // slice is array pointer + length - // Other then that it works exactly like regular array - slice: []int = static_array - - // We can't do a compile time Assert anymore - Assert(Len(slice) == 8) - Assert(slice[4] == 1) - - // After we loop and reassign slice values - // old static_array gets changed - // - // In this example, operator ';;' is used - // it inserts a new line, allows to write this - // example in a single line - for slice;; *it = 2 - - Assert(static_array[2] == 2) +/* +Static arrays are exactly like c arrays, difference is +we can easily then get a slice of that array. + +Slices are static arrays + length, they simplify array handling. +This allows us to pass an array into a function easily, then that function +can still access that length information easily. + +Passing a pointer to array + length is a common pattern in C. So it would +be nice if handling of that was simplified. +*/ + + + +main :: (): int + static_array: [8]int + + // We can get size of array using Length builtin + #Assert(Len(static_array) == 8) + + // Accessing values is like in C + // Variables are zeroed by default + Assert(static_array[1] == 0) + + element2 := static_array[2] + element0: int = static_array[0] + Assert(element0 == 0 && element2 == 0) + + // We can loop through arrays + // this implicitly defines 'it' variable + for static_array + *it = 1 + + // We set all variables to 1 so + Assert(static_array[6] == 1) + + // This is how slice is defined, no [] index in between brackets + // slice is array pointer + length + // Other then that it works exactly like regular array + slice: []int = static_array + + // We can't do a compile time Assert anymore + Assert(Len(slice) == 8) + Assert(slice[4] == 1) + + // After we loop and reassign slice values + // old static_array gets changed + // + // In this example, operator ';;' is used + // it inserts a new line, allows to write this + // example in a single line + for slice;; *it = 2 + + Assert(static_array[2] == 2) return 0 \ No newline at end of file diff --git a/build/examples/constant_expressions.core b/examples/constant_expressions.core similarity index 96% rename from build/examples/constant_expressions.core rename to examples/constant_expressions.core index 8732af8..a9f8ff6 100644 --- a/build/examples/constant_expressions.core +++ b/examples/constant_expressions.core @@ -1,40 +1,40 @@ -// We can bind module to a name -M :: #import "Multimedia.core" - -// You can bind struct to a name -MU :: M.MU - -// We can bind a lambda to a name -Start :: M.StartMultimedia -Update :: M.UpdateMultimedia - -// Other example of binding a lambda to a name -SomeOtherLambda :: () ;; pass -AliasOf :: SomeOtherLambda - -// We can bind a simple type to name, this type is -// exactly the same as int, typechecker doesn't complain -NewInt :: int - -// We can force it to complain using a '#strict' directive -// this makes a new type that requires casting, good for ids -// and stuff like that, normal int operations still work! -StrictInt :: #strict int - -SomeStruct :: struct ;; a: int -StructAlias :: SomeStruct - - -main :: (): int - some_struct: SomeStruct = {a = 10} - struct_alias: StructAlias = some_struct - Assert(struct_alias.a == 10) - - #Assert(SomeStruct == StructAlias) - #Assert(NewInt == int) - #Assert(StrictInt != int) - - Start(x = 1280, y = 720) - Update() - +// We can bind module to a name +M :: #import "Multimedia.core" + +// You can bind struct to a name +MU :: M.MU + +// We can bind a lambda to a name +Start :: M.StartMultimedia +Update :: M.UpdateMultimedia + +// Other example of binding a lambda to a name +SomeOtherLambda :: () ;; pass +AliasOf :: SomeOtherLambda + +// We can bind a simple type to name, this type is +// exactly the same as int, typechecker doesn't complain +NewInt :: int + +// We can force it to complain using a '#strict' directive +// this makes a new type that requires casting, good for ids +// and stuff like that, normal int operations still work! +StrictInt :: #strict int + +SomeStruct :: struct ;; a: int +StructAlias :: SomeStruct + + +main :: (): int + some_struct: SomeStruct = {a = 10} + struct_alias: StructAlias = some_struct + Assert(struct_alias.a == 10) + + #Assert(SomeStruct == StructAlias) + #Assert(NewInt == int) + #Assert(StrictInt != int) + + Start(x = 1280, y = 720) + Update() + return 0 \ No newline at end of file diff --git a/build/examples/drawing_to_screen_using_windows_api.core b/examples/drawing_to_screen_using_windows_api.core similarity index 96% rename from build/examples/drawing_to_screen_using_windows_api.core rename to examples/drawing_to_screen_using_windows_api.core index e256e44..79dd71e 100644 --- a/build/examples/drawing_to_screen_using_windows_api.core +++ b/examples/drawing_to_screen_using_windows_api.core @@ -1,76 +1,76 @@ -#import "Base.core" -#import "Arena.core" -#import "KERNEL32.core" -#import "GDI32.core" -#import "USER32.core" - -WinMain :: (hInstance: HINSTANCE, hPrevInstance: HINSTANCE, lpCmdLine: LPSTR, nShowCmd: int): int - screen_size_x: int = 1280 - screen_size_y: int = 720 - - arena: Arena - window_name := StringToString16(&arena, "Have a wonderful day!") - w := WNDCLASSW{ - lpfnWndProc = WindowProc, - hInstance = hInstance, - lpszClassName = window_name.str, - } - Assert(RegisterClassW(&w) != 0) - - window := CreateWindowExW( - dwExStyle = 0, hWndParent = 0, hMenu = 0, lpParam = 0, - X = CW_USEDEFAULT, Y = CW_USEDEFAULT, nWidth = screen_size_x, nHeight = screen_size_y, - lpClassName = window_name.str, - lpWindowName = window_name.str, - dwStyle = WS_OVERLAPPEDWINDOW, - hInstance = hInstance - ) - Assert(window != 0) - ShowWindow(window, nShowCmd) - window_dc := GetDC(window) - - header_size: U32 = sizeof(BITMAPINFOHEADER) - Assert(header_size == 40) - bminfo := BITMAPINFO{ - BITMAPINFOHEADER{ - biSize = header_size, - biWidth = screen_size_x->LONG, - biHeight = screen_size_y->LONG, - biPlanes = 1, - biBitCount = 32, - biCompression = BI_RGB, - biXPelsPerMeter = 1, - biYPelsPerMeter = 1, - } - } - - pixels: *U32 - bitmap_dib := CreateDIBSection(window_dc, &bminfo, DIB_RGB_COLORS, (&pixels)->**void, 0, 0) - bitmap_hdc := CreateCompatibleDC(window_dc) - - for AppIsRunning - msg: MSG - for PeekMessageW(&msg, window, 0, 0, PM_REMOVE) > 0 - TranslateMessage(&msg) - DispatchMessageW(&msg) - - for y := 0->int, y < screen_size_y, y+=1 - for x := 0->int, x < screen_size_x, x+=1 - pixels[x + y*screen_size_x] = 0xFFFF0000 - - SelectObject(bitmap_hdc, bitmap_dib) - BitBlt(window_dc, 0, 0, screen_size_x, screen_size_y, bitmap_hdc, 0, 0, SRCCOPY) - Sleep(100) - - if CStringCompare(lpCmdLine, "testing") - return 0 - return 0 - - -AppIsRunning := true -WindowProc :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRESULT - if msg == WM_DESTROY - PostQuitMessage(0) - AppIsRunning = false - return 0 +#import "Base.core" +#import "Arena.core" +#import "KERNEL32.core" +#import "GDI32.core" +#import "USER32.core" + +WinMain :: (hInstance: HINSTANCE, hPrevInstance: HINSTANCE, lpCmdLine: LPSTR, nShowCmd: int): int + screen_size_x: int = 1280 + screen_size_y: int = 720 + + arena: Arena + window_name := StringToString16(&arena, "Have a wonderful day!") + w := WNDCLASSW{ + lpfnWndProc = WindowProc, + hInstance = hInstance, + lpszClassName = window_name.str, + } + Assert(RegisterClassW(&w) != 0) + + window := CreateWindowExW( + dwExStyle = 0, hWndParent = 0, hMenu = 0, lpParam = 0, + X = CW_USEDEFAULT, Y = CW_USEDEFAULT, nWidth = screen_size_x, nHeight = screen_size_y, + lpClassName = window_name.str, + lpWindowName = window_name.str, + dwStyle = WS_OVERLAPPEDWINDOW, + hInstance = hInstance + ) + Assert(window != 0) + ShowWindow(window, nShowCmd) + window_dc := GetDC(window) + + header_size: U32 = sizeof(BITMAPINFOHEADER) + Assert(header_size == 40) + bminfo := BITMAPINFO{ + BITMAPINFOHEADER{ + biSize = header_size, + biWidth = screen_size_x->LONG, + biHeight = screen_size_y->LONG, + biPlanes = 1, + biBitCount = 32, + biCompression = BI_RGB, + biXPelsPerMeter = 1, + biYPelsPerMeter = 1, + } + } + + pixels: *U32 + bitmap_dib := CreateDIBSection(window_dc, &bminfo, DIB_RGB_COLORS, (&pixels)->**void, 0, 0) + bitmap_hdc := CreateCompatibleDC(window_dc) + + for AppIsRunning + msg: MSG + for PeekMessageW(&msg, window, 0, 0, PM_REMOVE) > 0 + TranslateMessage(&msg) + DispatchMessageW(&msg) + + for y := 0->int, y < screen_size_y, y+=1 + for x := 0->int, x < screen_size_x, x+=1 + pixels[x + y*screen_size_x] = 0xFFFF0000 + + SelectObject(bitmap_hdc, bitmap_dib) + BitBlt(window_dc, 0, 0, screen_size_x, screen_size_y, bitmap_hdc, 0, 0, SRCCOPY) + Sleep(100) + + if CStringCompare(lpCmdLine, "testing") + return 0 + return 0 + + +AppIsRunning := true +WindowProc :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRESULT + if msg == WM_DESTROY + PostQuitMessage(0) + AppIsRunning = false + return 0 else;; return DefWindowProcW(hwnd, msg, wparam, lparam) \ No newline at end of file diff --git a/build/examples/dynamic_typing.core b/examples/dynamic_typing.core similarity index 96% rename from build/examples/dynamic_typing.core rename to examples/dynamic_typing.core index 7c9c204..7186c04 100644 --- a/build/examples/dynamic_typing.core +++ b/examples/dynamic_typing.core @@ -1,55 +1,55 @@ -/* -Language enables implementing dynamic typing using operator overloads and -the Any type. Any type is a bundle of typeid and a pointer to value. - -Current semantics of the Any dictate that values that get assigned to -Any values, they get implicitly converted to a pointer and a type. - -a: Any = 10 // This sentence allocates 10 on the stack -a: Any = a + 10 // This also allocates on stack -a: Any = a // Does not allocate, takes pointer to a -a: Any = array[1] // Does not allocate, takes pointer to value in array - -The general picture you can take from this is that if we are referencing -something it will take a pointer to it. -If the operation results in a new value it will allocate the result on stack -and make a pointer out of it. Have to be mindful of the lifetime. -*/ - - -storage: [32]int -len : int - -"+" :: (a: Any, b: Any): Any - result: Any = storage[len++] - if a.type == int && b.type == int - *(result.data->*int) = *(a.data->*int) + *(b.data->*int) - return result - -"+" :: (a: Any, b: int): Any - result: Any = storage[len++] - if a.type == int - *(result.data->*int) = *(a.data->*int) + b - return result - -"==" :: (a: Any, b: int): bool - result := false - if a.type == int - result = *(a.data->*int) == b - return result - -"==" :: (a: Any, b: Any): bool - result := false - if a.type == int && b.type == int - result = *(a.data->*int) == *(b.data->*int) - return result - -main :: (): int - a: Any = 10 - b: Any = 20 - c := a + b - - Assert(c.type == int && c == 30) - Assert(a+b+a==c+(5+5)) - +/* +Language enables implementing dynamic typing using operator overloads and +the Any type. Any type is a bundle of typeid and a pointer to value. + +Current semantics of the Any dictate that values that get assigned to +Any values, they get implicitly converted to a pointer and a type. + +a: Any = 10 // This sentence allocates 10 on the stack +a: Any = a + 10 // This also allocates on stack +a: Any = a // Does not allocate, takes pointer to a +a: Any = array[1] // Does not allocate, takes pointer to value in array + +The general picture you can take from this is that if we are referencing +something it will take a pointer to it. +If the operation results in a new value it will allocate the result on stack +and make a pointer out of it. Have to be mindful of the lifetime. +*/ + + +storage: [32]int +len : int + +"+" :: (a: Any, b: Any): Any + result: Any = storage[len++] + if a.type == int && b.type == int + *(result.data->*int) = *(a.data->*int) + *(b.data->*int) + return result + +"+" :: (a: Any, b: int): Any + result: Any = storage[len++] + if a.type == int + *(result.data->*int) = *(a.data->*int) + b + return result + +"==" :: (a: Any, b: int): bool + result := false + if a.type == int + result = *(a.data->*int) == b + return result + +"==" :: (a: Any, b: Any): bool + result := false + if a.type == int && b.type == int + result = *(a.data->*int) == *(b.data->*int) + return result + +main :: (): int + a: Any = 10 + b: Any = 20 + c := a + b + + Assert(c.type == int && c == 30) + Assert(a+b+a==c+(5+5)) + return 0 \ No newline at end of file diff --git a/build/examples/language_basics.core b/examples/language_basics.core similarity index 96% rename from build/examples/language_basics.core rename to examples/language_basics.core index 08d1794..683a215 100644 --- a/build/examples/language_basics.core +++ b/examples/language_basics.core @@ -1,103 +1,103 @@ -main :: (): int - // Language has a bunch of standard builtin types: - // Signed integer types - s64val: S64 = 0 - s32val: S32 = 0 - s16val: S16 = 0 - s8val : S8 = 0 - intval: int = 0 - - // Unsigned integer types = U64, U32, U16, U8, - u64val: U64 = 0 - u32val: U32 = 0 - u16val: U16 = 0 - u8val : U8 = 0 - - // Floating point types = F64, F32 - f64val: F64 = 0 - f32val: F32 = 0 - - // String type = String - string_val: String = "String type" - cstring_val: *char = "CString type" - - // This is how we can assign variables - // There is no need for prefixes, compiler figures - // out the format by itself - signed_variable: S32 = 10 - unsigned_variable: U32 = 10 - - // We can also tell the compiler to infer the type - this_is_s64_by_default := 10 - this_is_f32_by_default := 10.1251 - this_is_string_by_default := "Thing" - - // Reassigning values is exactly like in other languages - this_is_s64_by_default = 20 - this_is_string_by_default = "Other_Thing" - this_is_f32_by_default = 15.1255 - - // @todo: Add type_of operator!!! - // Assert(type_of(this_is_string_by_default) == String) - // Assert(type_of(this_is_s64_by_default) == S64) - - // There are also constant bindings in the language. - // You can bind all sorts of constants to names this way. - INT_VALUE :: 10 - FLOAT_VALUE :: 124.125 - - // For constants we can mix and match different types - COMBINE_VALUE :: INT_VALUE + FLOAT_VALUE - - // When it comes to runtime variables it's a bit different - // To do this we need a cast - combining_types := this_is_s64_by_default->F32 + this_is_f32_by_default - - // Compound statements - // Struct is at the bottom of the file! - data1 := Data{ - a = 1, - d = 2 - } - data2: Data = { - a = 4, - b = 2, - } - - size0 := sizeof(Data) - size1 := sizeof(data1) - align0 := alignof(Data) - align1 := alignof(data1) - type0 := typeof(Data) - type1 := typeof(data1) - - Assert(s64val == 0 && s32val == 0 && s16val == 0 && s8val == 0 && intval == 0 && u64val == 0 && u32val == 0 && u16val == 0 && u8val == 0 && f64val == 0 && f32val == 0) - Assert(string_val[0] == 'S') - Assert(cstring_val[0] == 'C') - Assert(signed_variable == 10 && unsigned_variable == 10) - Assert(INT_VALUE == 10) - Assert(FLOAT_VALUE == 124.125) - Assert(this_is_f32_by_default == 15.1255) - Assert(combining_types == 15.1255 + 20) - - Assert(data1.a == 1) - Assert(data1.b == 0) - Assert(data1.c == 0) - Assert(data1.d == 2) - Assert(data2.a == 4) - Assert(data2.b == 2) - Assert(data2.c == 0) - Assert(data2.d == 0) - - Assert(size0 == size1) - Assert(align0 == align1) - Assert(type0 == type1) - Assert(typeof(data2) == Data) - - return 0 - -Data :: struct - a: S64 - b: S32 - c: S32 - d: S32 +main :: (): int + // Language has a bunch of standard builtin types: + // Signed integer types + s64val: S64 = 0 + s32val: S32 = 0 + s16val: S16 = 0 + s8val : S8 = 0 + intval: int = 0 + + // Unsigned integer types = U64, U32, U16, U8, + u64val: U64 = 0 + u32val: U32 = 0 + u16val: U16 = 0 + u8val : U8 = 0 + + // Floating point types = F64, F32 + f64val: F64 = 0 + f32val: F32 = 0 + + // String type = String + string_val: String = "String type" + cstring_val: *char = "CString type" + + // This is how we can assign variables + // There is no need for prefixes, compiler figures + // out the format by itself + signed_variable: S32 = 10 + unsigned_variable: U32 = 10 + + // We can also tell the compiler to infer the type + this_is_s64_by_default := 10 + this_is_f32_by_default := 10.1251 + this_is_string_by_default := "Thing" + + // Reassigning values is exactly like in other languages + this_is_s64_by_default = 20 + this_is_string_by_default = "Other_Thing" + this_is_f32_by_default = 15.1255 + + // @todo: Add type_of operator!!! + // Assert(type_of(this_is_string_by_default) == String) + // Assert(type_of(this_is_s64_by_default) == S64) + + // There are also constant bindings in the language. + // You can bind all sorts of constants to names this way. + INT_VALUE :: 10 + FLOAT_VALUE :: 124.125 + + // For constants we can mix and match different types + COMBINE_VALUE :: INT_VALUE + FLOAT_VALUE + + // When it comes to runtime variables it's a bit different + // To do this we need a cast + combining_types := this_is_s64_by_default->F32 + this_is_f32_by_default + + // Compound statements + // Struct is at the bottom of the file! + data1 := Data{ + a = 1, + d = 2 + } + data2: Data = { + a = 4, + b = 2, + } + + size0 := sizeof(Data) + size1 := sizeof(data1) + align0 := alignof(Data) + align1 := alignof(data1) + type0 := typeof(Data) + type1 := typeof(data1) + + Assert(s64val == 0 && s32val == 0 && s16val == 0 && s8val == 0 && intval == 0 && u64val == 0 && u32val == 0 && u16val == 0 && u8val == 0 && f64val == 0 && f32val == 0) + Assert(string_val[0] == 'S') + Assert(cstring_val[0] == 'C') + Assert(signed_variable == 10 && unsigned_variable == 10) + Assert(INT_VALUE == 10) + Assert(FLOAT_VALUE == 124.125) + Assert(this_is_f32_by_default == 15.1255) + Assert(combining_types == 15.1255 + 20) + + Assert(data1.a == 1) + Assert(data1.b == 0) + Assert(data1.c == 0) + Assert(data1.d == 2) + Assert(data2.a == 4) + Assert(data2.b == 2) + Assert(data2.c == 0) + Assert(data2.d == 0) + + Assert(size0 == size1) + Assert(align0 == align1) + Assert(type0 == type1) + Assert(typeof(data2) == Data) + + return 0 + +Data :: struct + a: S64 + b: S32 + c: S32 + d: S32 diff --git a/build/examples/operator_overloading.core b/examples/operator_overloading.core similarity index 96% rename from build/examples/operator_overloading.core rename to examples/operator_overloading.core index 9f9491b..875b84d 100644 --- a/build/examples/operator_overloading.core +++ b/examples/operator_overloading.core @@ -1,26 +1,26 @@ - -Vec3 :: struct;; x: F32; y: F32; z: F32 - -// We can define operator overloads for arbitrary types -// these are just regular lambdas/functions -"+" :: (a: Vec3, b: Vec3): Vec3 - return {a.x+b.x, a.y+b.y, a.z+b.z} - -// We can make a one liner out of these using ';;' operator -// which functions as a new line with indent -"-" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x-b.x, a.y-b.y, a.z-b.z} -"-" :: (a: Vec3): Vec3 ;; return {-a.x, -a.y, -a.z} - -main :: (): int - a := Vec3{1,1,1} - b := Vec3{2,3,4} - - // The expressions are replaced with the defined lambdas - c := a + b - Assert(c.x == 3 && c.y == 4 && c.z == 5) - d := -c - Assert(d.x == -3 && d.y == -4 && d.z == -5) - e := c - d - Assert(e.x == 6 && e.y == 8 && e.z == 10) - + +Vec3 :: struct;; x: F32; y: F32; z: F32 + +// We can define operator overloads for arbitrary types +// these are just regular lambdas/functions +"+" :: (a: Vec3, b: Vec3): Vec3 + return {a.x+b.x, a.y+b.y, a.z+b.z} + +// We can make a one liner out of these using ';;' operator +// which functions as a new line with indent +"-" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x-b.x, a.y-b.y, a.z-b.z} +"-" :: (a: Vec3): Vec3 ;; return {-a.x, -a.y, -a.z} + +main :: (): int + a := Vec3{1,1,1} + b := Vec3{2,3,4} + + // The expressions are replaced with the defined lambdas + c := a + b + Assert(c.x == 3 && c.y == 4 && c.z == 5) + d := -c + Assert(d.x == -3 && d.y == -4 && d.z == -5) + e := c - d + Assert(e.x == 6 && e.y == 8 && e.z == 10) + return 0 \ No newline at end of file diff --git a/build/examples/order_independent_declarations.core b/examples/order_independent_declarations.core similarity index 96% rename from build/examples/order_independent_declarations.core rename to examples/order_independent_declarations.core index 0eb001f..08b8b4c 100644 --- a/build/examples/order_independent_declarations.core +++ b/examples/order_independent_declarations.core @@ -1,74 +1,74 @@ -/* -Biggest problems with C/C++ are the header files and the fact that -top level declarations are required to be ordered. - -In C++ you simply cannot write code like this: - -struct Asset{ - Asset_Tag asset_tag; -}; - -enum Asset_Tag{ - ASSET_SOUND, - ASSET_IMAGE, -}; - -Even though it makes more sense to do that, I would rather first learn -about what Asset is rather then what tags it can have. C/C++ force on you -an order of declarations that doesn't match what you have in your head. - -The next problem are the header files. Frequently on top of writing code, -in C/C++ there is a need to maintain a header file. Header files contain -all the forward declarations of the functions that you are implementing. -Problem with this is that if you are changing something, potentially -you now need to change that in multiple places. It would be nice if I could -write a module that doesn't yet have any concrete spec but the functions -are available everywhere, immediately. On top of that those functions would -be nicely localized in a single file without thinking about what depends -on what, how to fit it in the stack of includes etc. It would be just nice -to tell the compiler to solve all those issues for us. -*/ - -// We can have main at the top level, this is something -// that is sometimes pretty tricky in C++ -main :: (): int - // Even though Asset is beneath main, this still works - // this feature is even nicer when we think about modules etc. - asset1 := make_sound() - asset2 := make_sound() - asset1.next_asset = &asset2 - return 0 - - // @todo: This does not work - // asset := []Asset{make_sound(), make_sound()} - // asset[0].next_sound = &asset[1] - - -// Recursion is a pain point for this kinds of algorithms -// As can be seen here it works very nicely -make_sound :: (should_exit_recursion: bool = false): Asset - if should_exit_recursion == true - asset: Asset - asset.tag = Asset_Tag.Sound - return asset - return make_sound(true) - -Asset :: struct - tag: Asset_Tag - - // Pointers to self work as expected - // this is always a pain point for these kinds of - // algorithms - next_asset: *Asset - -// enum #flag assigns default values differently from normal enums. -// It assigns a unique bit of a value to each enumeration. -// Default values go: 1, 2, 4, 8, 16, 32, 64 -Asset_Tag :: enum #flag - Sound - Image - Text - - - - +/* +Biggest problems with C/C++ are the header files and the fact that +top level declarations are required to be ordered. + +In C++ you simply cannot write code like this: + +struct Asset{ + Asset_Tag asset_tag; +}; + +enum Asset_Tag{ + ASSET_SOUND, + ASSET_IMAGE, +}; + +Even though it makes more sense to do that, I would rather first learn +about what Asset is rather then what tags it can have. C/C++ force on you +an order of declarations that doesn't match what you have in your head. + +The next problem are the header files. Frequently on top of writing code, +in C/C++ there is a need to maintain a header file. Header files contain +all the forward declarations of the functions that you are implementing. +Problem with this is that if you are changing something, potentially +you now need to change that in multiple places. It would be nice if I could +write a module that doesn't yet have any concrete spec but the functions +are available everywhere, immediately. On top of that those functions would +be nicely localized in a single file without thinking about what depends +on what, how to fit it in the stack of includes etc. It would be just nice +to tell the compiler to solve all those issues for us. +*/ + +// We can have main at the top level, this is something +// that is sometimes pretty tricky in C++ +main :: (): int + // Even though Asset is beneath main, this still works + // this feature is even nicer when we think about modules etc. + asset1 := make_sound() + asset2 := make_sound() + asset1.next_asset = &asset2 + return 0 + + // @todo: This does not work + // asset := []Asset{make_sound(), make_sound()} + // asset[0].next_sound = &asset[1] + + +// Recursion is a pain point for this kinds of algorithms +// As can be seen here it works very nicely +make_sound :: (should_exit_recursion: bool = false): Asset + if should_exit_recursion == true + asset: Asset + asset.tag = Asset_Tag.Sound + return asset + return make_sound(true) + +Asset :: struct + tag: Asset_Tag + + // Pointers to self work as expected + // this is always a pain point for these kinds of + // algorithms + next_asset: *Asset + +// enum #flag assigns default values differently from normal enums. +// It assigns a unique bit of a value to each enumeration. +// Default values go: 1, 2, 4, 8, 16, 32, 64 +Asset_Tag :: enum #flag + Sound + Image + Text + + + + diff --git a/examples/pathfind_visualizer b/examples/pathfind_visualizer new file mode 160000 index 0000000..caff4aa --- /dev/null +++ b/examples/pathfind_visualizer @@ -0,0 +1 @@ +Subproject commit caff4aaa2ee6a07c7b980203a2c113ae5d8f7407 diff --git a/build/examples/polymorphism.core b/examples/polymorphism.core similarity index 94% rename from build/examples/polymorphism.core rename to examples/polymorphism.core index 0afc241..0c49da7 100644 --- a/build/examples/polymorphism.core +++ b/examples/polymorphism.core @@ -1,92 +1,92 @@ - -/* @todo -QueueAddSLL(list: $List, node: $Node, $first = first, $last = last, $next = next) - if list.first == 0 - list.first = list.last = node - else - list.last = list.last.next = node -*/ - -Array :: struct($T: Type) - data: *T - len: int - cap: int - -Tuple :: struct($A: Type, $B: Type) - a: A - b: B - -Triple :: struct($A: Type, $B: Type, $C: Type) - a: A - b: B - c: C - - -Variant :: union($A: Type, $B: Type, $C: Type) - a: A - b: B - c: C - -MakeArray :: (a: *int, count: int): Array(int) - result := Array(int) { - data = a, - len = count, - cap = count - } - return result - -MultipleArgs :: (): Tuple(int, F32) - return {32, 32} - -PolyLambda :: ($T: Type = *int): T - return 0 - -PolyType :: (a: $T): T - return a - -GetCount :: (a: int): int - return a - -C :: #import "LibC.core" - -Add :: (arr: *Array($T), val: T) - if arr.cap == 0 - arr.cap = 16 - arr.data = C.malloc(sizeof(T)->U64 * arr.cap->U64) - arr.data[arr.len++] = val - -main :: (argc: int, argv: **char): int - buff: *int - array: Array(S64) - second_array: Array(int) - third_array: Array(int) - fourth: Array(F32) - fifth: Array(F32) - sixth: Array(Array(F32)) - seventh: Variant(int, F32, S64) - - test_a := int - test := *int - Assert(test_a != test) - - // c := MakeArray(buff, GetCount(GetCount(32))) - - a, b := MultipleArgs() - Assert(a == 32 && b == 32) - - Add(&array, 32) - Add(&second_array, 32) - Add(&third_array, 32) - Add(&fourth, 32) - Add(&fifth, 32) - Add(&sixth, {}) - - value := PolyLambda(**int) - PolyType_r1 := PolyType(10) - PolyType_r2 := PolyType(int) - PolyType_r3 := PolyType(test) - PolyType_r4 := PolyType(test_a) - PolyType_r5 := PolyType(sixth) - PolyType_r6 := PolyType(seventh) - + +/* @todo +QueueAddSLL(list: $List, node: $Node, $first = first, $last = last, $next = next) + if list.first == 0 + list.first = list.last = node + else + list.last = list.last.next = node +*/ + +Array :: struct($T: Type) + data: *T + len: int + cap: int + +Tuple :: struct($A: Type, $B: Type) + a: A + b: B + +Triple :: struct($A: Type, $B: Type, $C: Type) + a: A + b: B + c: C + + +Variant :: union($A: Type, $B: Type, $C: Type) + a: A + b: B + c: C + +MakeArray :: (a: *int, count: int): Array(int) + result := Array(int) { + data = a, + len = count, + cap = count + } + return result + +MultipleArgs :: (): Tuple(int, F32) + return {32, 32} + +PolyLambda :: ($T: Type = *int): T + return 0 + +PolyType :: (a: $T): T + return a + +GetCount :: (a: int): int + return a + +C :: #import "LibC.core" + +Add :: (arr: *Array($T), val: T) + if arr.cap == 0 + arr.cap = 16 + arr.data = C.malloc(sizeof(T)->U64 * arr.cap->U64) + arr.data[arr.len++] = val + +main :: (argc: int, argv: **char): int + buff: *int + array: Array(S64) + second_array: Array(int) + third_array: Array(int) + fourth: Array(F32) + fifth: Array(F32) + sixth: Array(Array(F32)) + seventh: Variant(int, F32, S64) + + test_a := int + test := *int + Assert(test_a != test) + + // c := MakeArray(buff, GetCount(GetCount(32))) + + a, b := MultipleArgs() + Assert(a == 32 && b == 32) + + Add(&array, 32) + Add(&second_array, 32) + Add(&third_array, 32) + Add(&fourth, 32) + Add(&fifth, 32) + Add(&sixth, {}) + + value := PolyLambda(**int) + PolyType_r1 := PolyType(10) + PolyType_r2 := PolyType(int) + PolyType_r3 := PolyType(test) + PolyType_r4 := PolyType(test_a) + PolyType_r5 := PolyType(sixth) + PolyType_r6 := PolyType(seventh) + return 0 \ No newline at end of file diff --git a/build/examples/push_struct.core b/examples/push_struct.core similarity index 96% rename from build/examples/push_struct.core rename to examples/push_struct.core index 78effa3..1b657de 100644 --- a/build/examples/push_struct.core +++ b/examples/push_struct.core @@ -1,15 +1,15 @@ -MA :: #import "Arena.core" - -PushStruct :: (a: *MA.Arena, $K: Type, $T: Type): *T - size := sizeof(T) - result := MA.PushSize(a, size->U64) - return result->*T - -main :: (argc: int, argv: **char): int - arena: MA.Arena - a: *int = PushStruct(&arena, int, int) - b: *F32 = PushStruct(&arena, int, F32) - padding := sizeof(int) - Assert(arena.len->int == (sizeof(int) + sizeof(F32) + padding)) - +MA :: #import "Arena.core" + +PushStruct :: (a: *MA.Arena, $K: Type, $T: Type): *T + size := sizeof(T) + result := MA.PushSize(a, size->U64) + return result->*T + +main :: (argc: int, argv: **char): int + arena: MA.Arena + a: *int = PushStruct(&arena, int, int) + b: *F32 = PushStruct(&arena, int, F32) + padding := sizeof(int) + Assert(arena.len->int == (sizeof(int) + sizeof(F32) + padding)) + return 0 \ No newline at end of file diff --git a/build/examples/raymarcher.core b/examples/raymarcher.core similarity index 96% rename from build/examples/raymarcher.core rename to examples/raymarcher.core index 9ab5c09..a053b28 100644 --- a/build/examples/raymarcher.core +++ b/examples/raymarcher.core @@ -1,210 +1,210 @@ -F :: #import "MathF32.core" -V3 :: #import "MathVec3.core"; Vec3 :: V3.Vec3 -V2 :: #import "MathVec2.core"; Vec2 :: V2.Vec2 - -Epsilon :: 0.00001 -Screen : *U32 -X : int -Y : int -TotalTime: F64 -LightPos := Vec3{2,4,2} - -SphereSDF :: (pos: Vec3): F32 - result := V3.Length(pos) - 1.0 - return result - -Raymarcher_Update :: () - up := Vec3{0, 1, 0} - forward := Vec3{0, 0, -1} - side := V3.Normalize(V3.Cross(forward, up)) - - LightPos.x = F.Cos(TotalTime->F32)*4 - LightPos.y = F.Sin(TotalTime->F32)*4 - - ambient_color := Vec3{0.2,0.2,0.2} - diffuse_color := Vec3{0.7,0.2,0.2} - specular_color := Vec3{1,1,1} - eye := Vec3{0, 0, 2} - light_intensity :: 1.2->F32 - - Xf := 1 / X->F32 - Yf := 1 / Y->F32 - ratio := X->F32 / Y->F32 - for y := 0, y < Y, y+=1 - for x := 0, x < X, x+=1 - uv := Vec3{x->F32 * Xf * 2 - 1, y->F32 * Yf * 2 - 1, 1.0} - uv.x *= ratio - dir := V3.Normalize(Vec3{V3.Dot(side, uv), V3.Dot(up, uv), V3.Dot(forward, uv)}) - - t: F32 - end: F32 = 100.0 - hit := true - p: Vec3 - for i := 0, i < 255, i+=1 - p = eye + dir*t - - distance := SphereSDF(p) - if distance < Epsilon - break - - t += distance - if distance >= end - hit = false - break - - if hit - normal := V3.Normalize(Vec3{ - SphereSDF({p.x + Epsilon, p.y, p.z}) - SphereSDF({p.x - Epsilon, p.y, p.z}), - SphereSDF({p.x, p.y + Epsilon, p.z}) - SphereSDF({p.x, p.y - Epsilon, p.z}), - SphereSDF({p.x, p.y, p.z + Epsilon}) - SphereSDF({p.x, p.y, p.z - Epsilon}), - }) - - light_to_point := V3.Normalize(LightPos - p) - eye_to_point := V3.Normalize(eye - p) - reflected_light := V3.Normalize(V3.Reflect(V3.Negate(light_to_point), normal)) - - ambient :: 0.2->F32 - diffuse := V3.Dot(normal, light_to_point) - - color := ambient_color*ambient->F32 - if diffuse > Epsilon - color = color + diffuse_color*diffuse - - specular := V3.Dot(reflected_light, eye_to_point) - if specular > Epsilon - specular = specular*specular*specular*specular - color = color + specular_color*specular*0.2->F32 - color = color * light_intensity - - // Gamma correction - color.x = F.SquareRoot(color.x) - color.y = F.SquareRoot(color.y) - color.z = F.SquareRoot(color.z) - Screen[x + y*X] = V3.ConvertToARGB(color) - - else;; Screen[x + y*X] = 0 - - - - -///////////////////////////////////// -// -// Windows specific code -// - -#import "Base.core" -#import "Arena.core" -#import "OS$OS.core" -#import "KERNEL32.core" -#import "GDI32.core" -#import "USER32.core" -#import "WINMM.core" - - -AppIsRunning := true -WindowProc :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRESULT - if msg == WM_DESTROY - PostQuitMessage(0) - AppIsRunning = false - return 0 - else;; return DefWindowProcW(hwnd, msg, wparam, lparam) - -WinMain :: (hInstance: HINSTANCE, hPrevInstance: HINSTANCE, lpCmdLine: LPSTR, nShowCmd: int): int - if good_scheduling := false, timeBeginPeriod(1) == TIMERR_NOERROR - good_scheduling = true - - dpi_aware_set := SetProcessDPIAware() - Assert(dpi_aware_set != 0) - - arena: Arena - - window_name := StringToString16(&arena, "Have a wonderful day! 你好世界 ") - w := WNDCLASSW{ - lpfnWndProc = WindowProc, - hInstance = hInstance, - lpszClassName = window_name.str, - } - Assert(RegisterClassW(&w) != 0) - - screen_size: V2.Vec2I = {1280, 720} - window := CreateWindowExW( - dwExStyle = 0, hWndParent = 0, hMenu = 0, lpParam = 0, - X = CW_USEDEFAULT, Y = CW_USEDEFAULT, nWidth = screen_size.x->int, nHeight = screen_size.y->int, - lpClassName = window_name.str, - lpWindowName = window_name.str, - dwStyle = WS_OVERLAPPEDWINDOW, - hInstance = hInstance - ) - Assert(window != 0) - ShowWindow(window, nShowCmd) - - window_dc := GetDC(window) - bitmap := CreateBitmap(screen_size) - - requested_time_per_frame: F64 = 1.0 / 60.0 - frame_start_time := Time() - frame_number: int - for AppIsRunning - msg: MSG - for PeekMessageW(&msg, window, 0, 0, PM_REMOVE) > 0 - TranslateMessage(&msg) - DispatchMessageW(&msg) - - Screen = bitmap.data; X = bitmap.size.x; Y = bitmap.size.y - Raymarcher_Update() - - SelectObject(bitmap.hdc, bitmap.dib) - BitBlt(window_dc, 0, 0, (bitmap.size.x)->int, (bitmap.size.y)->int, bitmap.hdc, 0, 0, SRCCOPY) - - frame_time := Time() - frame_start_time - frame_number += 1 - TotalTime += frame_time - if frame_time < requested_time_per_frame - if good_scheduling - time_to_sleep := (requested_time_per_frame - frame_time) * 1000 - if time_to_sleep > 0 - time_to_sleep_dword := time_to_sleep->DWORD - // @check if time_to_sleep_dword truncates down - Sleep(time_to_sleep_dword) - - new_frame_time := Time() - for new_frame_time < requested_time_per_frame - new_frame_time = Time() - frame_start_time - - frame_time = new_frame_time - - if CStringCompare(lpCmdLine, "testing") - return 0 - return 0 - - -Windows_Bitmap :: struct - size: V2.Vec2I - data: *U32 - hdc: HDC - dib: HBITMAP - -CreateBitmap :: (size: V2.Vec2I, bottom_up: bool = true): Windows_Bitmap - result: Windows_Bitmap = {size = size} - if bottom_up == false - result.size.y = -result.size.y - - header_size: U32 = sizeof(BITMAPINFOHEADER) - Assert(header_size == 40) - bminfo := BITMAPINFO{ - BITMAPINFOHEADER{ - biSize = header_size, - biWidth = size.x->LONG, - biHeight = size.y->LONG, - biPlanes = 1, - biBitCount = 32, - biCompression = BI_RGB, - biXPelsPerMeter = 1, - biYPelsPerMeter = 1, - } - } - - hdc := GetDC(0) - result.dib = CreateDIBSection(hdc, &bminfo, DIB_RGB_COLORS, (&result.data)->**void, 0, 0) - result.hdc = CreateCompatibleDC(hdc) - return result +F :: #import "MathF32.core" +V3 :: #import "MathVec3.core"; Vec3 :: V3.Vec3 +V2 :: #import "MathVec2.core"; Vec2 :: V2.Vec2 + +Epsilon :: 0.00001 +Screen : *U32 +X : int +Y : int +TotalTime: F64 +LightPos := Vec3{2,4,2} + +SphereSDF :: (pos: Vec3): F32 + result := V3.Length(pos) - 1.0 + return result + +Raymarcher_Update :: () + up := Vec3{0, 1, 0} + forward := Vec3{0, 0, -1} + side := V3.Normalize(V3.Cross(forward, up)) + + LightPos.x = F.Cos(TotalTime->F32)*4 + LightPos.y = F.Sin(TotalTime->F32)*4 + + ambient_color := Vec3{0.2,0.2,0.2} + diffuse_color := Vec3{0.7,0.2,0.2} + specular_color := Vec3{1,1,1} + eye := Vec3{0, 0, 2} + light_intensity :: 1.2->F32 + + Xf := 1 / X->F32 + Yf := 1 / Y->F32 + ratio := X->F32 / Y->F32 + for y := 0, y < Y, y+=1 + for x := 0, x < X, x+=1 + uv := Vec3{x->F32 * Xf * 2 - 1, y->F32 * Yf * 2 - 1, 1.0} + uv.x *= ratio + dir := V3.Normalize(Vec3{V3.Dot(side, uv), V3.Dot(up, uv), V3.Dot(forward, uv)}) + + t: F32 + end: F32 = 100.0 + hit := true + p: Vec3 + for i := 0, i < 255, i+=1 + p = eye + dir*t + + distance := SphereSDF(p) + if distance < Epsilon + break + + t += distance + if distance >= end + hit = false + break + + if hit + normal := V3.Normalize(Vec3{ + SphereSDF({p.x + Epsilon, p.y, p.z}) - SphereSDF({p.x - Epsilon, p.y, p.z}), + SphereSDF({p.x, p.y + Epsilon, p.z}) - SphereSDF({p.x, p.y - Epsilon, p.z}), + SphereSDF({p.x, p.y, p.z + Epsilon}) - SphereSDF({p.x, p.y, p.z - Epsilon}), + }) + + light_to_point := V3.Normalize(LightPos - p) + eye_to_point := V3.Normalize(eye - p) + reflected_light := V3.Normalize(V3.Reflect(V3.Negate(light_to_point), normal)) + + ambient :: 0.2->F32 + diffuse := V3.Dot(normal, light_to_point) + + color := ambient_color*ambient->F32 + if diffuse > Epsilon + color = color + diffuse_color*diffuse + + specular := V3.Dot(reflected_light, eye_to_point) + if specular > Epsilon + specular = specular*specular*specular*specular + color = color + specular_color*specular*0.2->F32 + color = color * light_intensity + + // Gamma correction + color.x = F.SquareRoot(color.x) + color.y = F.SquareRoot(color.y) + color.z = F.SquareRoot(color.z) + Screen[x + y*X] = V3.ConvertToARGB(color) + + else;; Screen[x + y*X] = 0 + + + + +///////////////////////////////////// +// +// Windows specific code +// + +#import "Base.core" +#import "Arena.core" +#import "OS$OS.core" +#import "KERNEL32.core" +#import "GDI32.core" +#import "USER32.core" +#import "WINMM.core" + + +AppIsRunning := true +WindowProc :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRESULT + if msg == WM_DESTROY + PostQuitMessage(0) + AppIsRunning = false + return 0 + else;; return DefWindowProcW(hwnd, msg, wparam, lparam) + +WinMain :: (hInstance: HINSTANCE, hPrevInstance: HINSTANCE, lpCmdLine: LPSTR, nShowCmd: int): int + if good_scheduling := false, timeBeginPeriod(1) == TIMERR_NOERROR + good_scheduling = true + + dpi_aware_set := SetProcessDPIAware() + Assert(dpi_aware_set != 0) + + arena: Arena + + window_name := StringToString16(&arena, "Have a wonderful day! 你好世界 ") + w := WNDCLASSW{ + lpfnWndProc = WindowProc, + hInstance = hInstance, + lpszClassName = window_name.str, + } + Assert(RegisterClassW(&w) != 0) + + screen_size: V2.Vec2I = {1280, 720} + window := CreateWindowExW( + dwExStyle = 0, hWndParent = 0, hMenu = 0, lpParam = 0, + X = CW_USEDEFAULT, Y = CW_USEDEFAULT, nWidth = screen_size.x->int, nHeight = screen_size.y->int, + lpClassName = window_name.str, + lpWindowName = window_name.str, + dwStyle = WS_OVERLAPPEDWINDOW, + hInstance = hInstance + ) + Assert(window != 0) + ShowWindow(window, nShowCmd) + + window_dc := GetDC(window) + bitmap := CreateBitmap(screen_size) + + requested_time_per_frame: F64 = 1.0 / 60.0 + frame_start_time := Time() + frame_number: int + for AppIsRunning + msg: MSG + for PeekMessageW(&msg, window, 0, 0, PM_REMOVE) > 0 + TranslateMessage(&msg) + DispatchMessageW(&msg) + + Screen = bitmap.data; X = bitmap.size.x; Y = bitmap.size.y + Raymarcher_Update() + + SelectObject(bitmap.hdc, bitmap.dib) + BitBlt(window_dc, 0, 0, (bitmap.size.x)->int, (bitmap.size.y)->int, bitmap.hdc, 0, 0, SRCCOPY) + + frame_time := Time() - frame_start_time + frame_number += 1 + TotalTime += frame_time + if frame_time < requested_time_per_frame + if good_scheduling + time_to_sleep := (requested_time_per_frame - frame_time) * 1000 + if time_to_sleep > 0 + time_to_sleep_dword := time_to_sleep->DWORD + // @check if time_to_sleep_dword truncates down + Sleep(time_to_sleep_dword) + + new_frame_time := Time() + for new_frame_time < requested_time_per_frame + new_frame_time = Time() - frame_start_time + + frame_time = new_frame_time + + if CStringCompare(lpCmdLine, "testing") + return 0 + return 0 + + +Windows_Bitmap :: struct + size: V2.Vec2I + data: *U32 + hdc: HDC + dib: HBITMAP + +CreateBitmap :: (size: V2.Vec2I, bottom_up: bool = true): Windows_Bitmap + result: Windows_Bitmap = {size = size} + if bottom_up == false + result.size.y = -result.size.y + + header_size: U32 = sizeof(BITMAPINFOHEADER) + Assert(header_size == 40) + bminfo := BITMAPINFO{ + BITMAPINFOHEADER{ + biSize = header_size, + biWidth = size.x->LONG, + biHeight = size.y->LONG, + biPlanes = 1, + biBitCount = 32, + biCompression = BI_RGB, + biXPelsPerMeter = 1, + biYPelsPerMeter = 1, + } + } + + hdc := GetDC(0) + result.dib = CreateDIBSection(hdc, &bminfo, DIB_RGB_COLORS, (&result.data)->**void, 0, 0) + result.hdc = CreateCompatibleDC(hdc) + return result diff --git a/build/examples/runtime_type_information.core b/examples/runtime_type_information.core similarity index 96% rename from build/examples/runtime_type_information.core rename to examples/runtime_type_information.core index 23f4d97..406c1fb 100644 --- a/build/examples/runtime_type_information.core +++ b/examples/runtime_type_information.core @@ -1,65 +1,65 @@ -main :: (): int - // Let's say we have a type - some_type := S64 - - // this function call allows us to get extensive type information - // about this type. For example this information allows us to walk - // the type tree, pretty print all values in that type, along with their sizes. - // Other use cases allow us to do a type safe printf - type_info: *Type_Info = GetTypeInfo(some_type) - - // It can be null, requiring the type information would be a bit unwise - // this is a lot of data - if !type_info - return 1 - - if type_info.type == S64 - // We can use sizeof and alignof operators - // to figure out the type alignment and it's size - Assert(type_info.size == sizeof(S64)) - Assert(type_info.align == alignof(S64)) - - else;; Assert(false, "We expected S64 here! What a boomer!") - - // - // @todo: This should work - // - // any_thing: Any = 10 - - - // - // Any type allows us to combine a value pointer and - // it's corresponding type_id - // - value_to_be_wrapped := 10 - any_value: Any = value_to_be_wrapped - - if any_value.type == int - *(any_value.data->*int) = 20 - else ;; Assert(false, "No bueno") - - Assert(*(any_value.data->*int) == 20) - - letter := GetFirstLetterOfType(value_to_be_wrapped) - Assert(letter == 'I') - return 0 - -GetFirstLetterOfType :: (a: Any): U8 - type_info := GetTypeInfo(a.type) - if !type_info - return '-' - - result: U8 - switch type_info.kind - Type_Info_Kind.S64, Type_Info_Kind.S32, Type_Info_Kind.S16, Type_Info_Kind.S8, Type_Info_Kind.INT - result = 'I' - Type_Info_Kind.U64, Type_Info_Kind.U32, Type_Info_Kind.U16, Type_Info_Kind.U8 - result = 'U' - Type_Info_Kind.F64 - result = 'F' - Type_Info_Kind.POINTER - result = '*' - default;; result = '-' - - return result - +main :: (): int + // Let's say we have a type + some_type := S64 + + // this function call allows us to get extensive type information + // about this type. For example this information allows us to walk + // the type tree, pretty print all values in that type, along with their sizes. + // Other use cases allow us to do a type safe printf + type_info: *Type_Info = GetTypeInfo(some_type) + + // It can be null, requiring the type information would be a bit unwise + // this is a lot of data + if !type_info + return 1 + + if type_info.type == S64 + // We can use sizeof and alignof operators + // to figure out the type alignment and it's size + Assert(type_info.size == sizeof(S64)) + Assert(type_info.align == alignof(S64)) + + else;; Assert(false, "We expected S64 here! What a boomer!") + + // + // @todo: This should work + // + // any_thing: Any = 10 + + + // + // Any type allows us to combine a value pointer and + // it's corresponding type_id + // + value_to_be_wrapped := 10 + any_value: Any = value_to_be_wrapped + + if any_value.type == int + *(any_value.data->*int) = 20 + else ;; Assert(false, "No bueno") + + Assert(*(any_value.data->*int) == 20) + + letter := GetFirstLetterOfType(value_to_be_wrapped) + Assert(letter == 'I') + return 0 + +GetFirstLetterOfType :: (a: Any): U8 + type_info := GetTypeInfo(a.type) + if !type_info + return '-' + + result: U8 + switch type_info.kind + Type_Info_Kind.S64, Type_Info_Kind.S32, Type_Info_Kind.S16, Type_Info_Kind.S8, Type_Info_Kind.INT + result = 'I' + Type_Info_Kind.U64, Type_Info_Kind.U32, Type_Info_Kind.U16, Type_Info_Kind.U8 + result = 'U' + Type_Info_Kind.F64 + result = 'F' + Type_Info_Kind.POINTER + result = '*' + default;; result = '-' + + return result + diff --git a/build/examples/types_as_first_class_values.core b/examples/types_as_first_class_values.core similarity index 96% rename from build/examples/types_as_first_class_values.core rename to examples/types_as_first_class_values.core index c5da547..ab6a4d4 100644 --- a/build/examples/types_as_first_class_values.core +++ b/examples/types_as_first_class_values.core @@ -1,48 +1,48 @@ -main :: (): int - // Types can be evaluated at compile time for equality - #Assert(int == int) - #Assert(int != char) - #Assert(*char == *char) - - // They can also be evaluated at runtime, they basically get - // replaced with type ids, which are just unique integers assigned - // to each type - Assert(int == int) - Assert(int != char) - Assert(*char == *char) - - // We can assign types to compile time variable constants - New_Type :: int - - // This is a loose association - thing: int = 10 - new_type_thing: New_Type = thing - #Assert(New_Type == int) - - // to force typechecker to treat$ - // both of these types as different we need to add a #strict directive - Strict_Type :: #strict int - - // new_strict_type_thing: Strict_Type = thing // This produces a compile time type error - // But this works - strict_thing: Strict_Type = 10 - #Assert(Strict_Type != int) - - // If we want to use those types together we need to cast - Assert(new_type_thing + strict_thing->int != 0) - - // We can also assign types to runtime variables, there is a special type for that - some_type: Type = int - some_type_implicit := char - - // These can be checked for equality using ifs and switches - if some_type == char - pass - - elif some_type_implicit == char - pass - - return 0 - - - +main :: (): int + // Types can be evaluated at compile time for equality + #Assert(int == int) + #Assert(int != char) + #Assert(*char == *char) + + // They can also be evaluated at runtime, they basically get + // replaced with type ids, which are just unique integers assigned + // to each type + Assert(int == int) + Assert(int != char) + Assert(*char == *char) + + // We can assign types to compile time variable constants + New_Type :: int + + // This is a loose association + thing: int = 10 + new_type_thing: New_Type = thing + #Assert(New_Type == int) + + // to force typechecker to treat$ + // both of these types as different we need to add a #strict directive + Strict_Type :: #strict int + + // new_strict_type_thing: Strict_Type = thing // This produces a compile time type error + // But this works + strict_thing: Strict_Type = 10 + #Assert(Strict_Type != int) + + // If we want to use those types together we need to cast + Assert(new_type_thing + strict_thing->int != 0) + + // We can also assign types to runtime variables, there is a special type for that + some_type: Type = int + some_type_implicit := char + + // These can be checked for equality using ifs and switches + if some_type == char + pass + + elif some_type_implicit == char + pass + + return 0 + + + diff --git a/build/examples/unions.core b/examples/unions.core similarity index 93% rename from build/examples/unions.core rename to examples/unions.core index b35b9cc..1c41f0a 100644 --- a/build/examples/unions.core +++ b/examples/unions.core @@ -1,40 +1,40 @@ - -U :: union - a: F64 - b: F32 - -C :: struct - a: int - b: int - -main :: (argc: int, argv: **char): int - memes: U - memes.b = 10 - Assert(memes.b == 10) - Assert(memes.a != 0) - - compound: U = {b = 10.0} - Assert(compound.b == 10) - - t := U - ti := GetTypeInfo(t) - Assert(ti.size == sizeof(U)) - - for ti.struct_members - Assert(it.offset == 0) - - ti_it := GetTypeInfo(it.type) - Assert(ti_it.size != 0) - - -/* @reproduction @todo -``` -examples/unions.core - Error! Couldn't infer type of compound expression - c = {10} -``` - - c: C - c = {10} -*/ - - return 0 + +U :: union + a: F64 + b: F32 + +C :: struct + a: int + b: int + +main :: (argc: int, argv: **char): int + memes: U + memes.b = 10 + Assert(memes.b == 10) + Assert(memes.a != 0) + + compound: U = {b = 10.0} + Assert(compound.b == 10) + + t := U + ti := GetTypeInfo(t) + Assert(ti.size == sizeof(U)) + + for ti.struct_members + Assert(it.offset == 0) + + ti_it := GetTypeInfo(it.type) + Assert(ti_it.size != 0) + + +/* @reproduction @todo +``` +examples/unions.core - Error! Couldn't infer type of compound expression + c = {10} +``` + + c: C + c = {10} +*/ + + return 0 diff --git a/build/examples/using_multimedia.core b/examples/using_multimedia.core similarity index 96% rename from build/examples/using_multimedia.core rename to examples/using_multimedia.core index 90c3099..5981506 100644 --- a/build/examples/using_multimedia.core +++ b/examples/using_multimedia.core @@ -1,12 +1,12 @@ -#import "Multimedia.core" - -main :: (): int - StartMultimedia(title = "Hello people!") - for UpdateMultimedia() - if Mu.key[Key.Escape].down ;; Mu.quit = true - - for y := 0, y < Mu.window.y, y+=1 - for x := 0, x < Mu.window.x, x+=1 - Mu.screen[x + y*Mu.window.x] = 0xFFFFFF00 - +#import "Multimedia.core" + +main :: (): int + StartMultimedia(title = "Hello people!") + for UpdateMultimedia() + if Mu.key[Key.Escape].down ;; Mu.quit = true + + for y := 0, y < Mu.window.y, y+=1 + for x := 0, x < Mu.window.x, x+=1 + Mu.screen[x + y*Mu.window.x] = 0xFFFFFF00 + return 0 \ No newline at end of file diff --git a/info.bat b/info.bat deleted file mode 100644 index a81d610..0000000 --- a/info.bat +++ /dev/null @@ -1,2 +0,0 @@ -cloc *.hpp *.cpp *.py *.h -git rev-list --all --count \ No newline at end of file diff --git a/build/modules/Arena.core b/modules/Arena.core similarity index 96% rename from build/modules/Arena.core rename to modules/Arena.core index 27b7931..a75c22b 100644 --- a/build/modules/Arena.core +++ b/modules/Arena.core @@ -1,50 +1,50 @@ -OS :: #import "OS$OS.core" -Base :: #import "Base.core" -ArenaDI: U64 - -ADDITIONAL_COMMIT_SIZE :: 1024*1024 -DEFAULT_RESERVE_SIZE :: 1024*1024*1024 -DEFAULT_ALIGNMENT :: 8 - -Arena :: struct - di: U64 // @debug_id - memory: OS.Memory - alignment: U64 - len: U64 - -Init :: (a: *Arena) - a.memory = OS.Reserve(DEFAULT_RESERVE_SIZE) - a.alignment = DEFAULT_ALIGNMENT - a.di = ArenaDI++ - -FromBuffer :: (buffer: []U8): Arena - a: Arena - a.memory.reserve = Len(buffer)->U64 - a.memory.commit = Len(buffer)->U64 - a.alignment = DEFAULT_ALIGNMENT - a.di = ArenaDI++ - return a - -PushSize :: (a: *Arena, size: Base.SizeU): *void - generous_size := size + a.alignment - if a.len + generous_size > a.memory.commit - if a.memory.reserve == 0 - Init(a) - result := OS.Commit(&a.memory, generous_size + ADDITIONAL_COMMIT_SIZE) - Assert(result == true) - a.len = Base.AlignUp(a.len, a.alignment) - Assert(a.memory.reserve > a.len + a.memory.commit) - result: *void = a.memory.data + a.len - a.len += size - return result - -Release :: (a: *Arena) - OS.Release(&a.memory) - -PushArray :: (a: *Arena, count: int, $T: Type): *T - result := PushSize(a, sizeof(T) * count->U64) - return result->*T - -PushStruct :: (a: *Arena, $T: Type): *T - result := PushSize(a, sizeof(T)) +OS :: #import "OS$OS.core" +Base :: #import "Base.core" +ArenaDI: U64 + +ADDITIONAL_COMMIT_SIZE :: 1024*1024 +DEFAULT_RESERVE_SIZE :: 1024*1024*1024 +DEFAULT_ALIGNMENT :: 8 + +Arena :: struct + di: U64 // @debug_id + memory: OS.Memory + alignment: U64 + len: U64 + +Init :: (a: *Arena) + a.memory = OS.Reserve(DEFAULT_RESERVE_SIZE) + a.alignment = DEFAULT_ALIGNMENT + a.di = ArenaDI++ + +FromBuffer :: (buffer: []U8): Arena + a: Arena + a.memory.reserve = Len(buffer)->U64 + a.memory.commit = Len(buffer)->U64 + a.alignment = DEFAULT_ALIGNMENT + a.di = ArenaDI++ + return a + +PushSize :: (a: *Arena, size: Base.SizeU): *void + generous_size := size + a.alignment + if a.len + generous_size > a.memory.commit + if a.memory.reserve == 0 + Init(a) + result := OS.Commit(&a.memory, generous_size + ADDITIONAL_COMMIT_SIZE) + Assert(result == true) + a.len = Base.AlignUp(a.len, a.alignment) + Assert(a.memory.reserve > a.len + a.memory.commit) + result: *void = a.memory.data + a.len + a.len += size + return result + +Release :: (a: *Arena) + OS.Release(&a.memory) + +PushArray :: (a: *Arena, count: int, $T: Type): *T + result := PushSize(a, sizeof(T) * count->U64) + return result->*T + +PushStruct :: (a: *Arena, $T: Type): *T + result := PushSize(a, sizeof(T)) return result->*T \ No newline at end of file diff --git a/build/modules/Base.core b/modules/Base.core similarity index 97% rename from build/modules/Base.core rename to modules/Base.core index 7f0f97a..e53b2cd 100644 --- a/build/modules/Base.core +++ b/modules/Base.core @@ -1,124 +1,124 @@ -#import "Arena.core" -OS :: #import "OS$OS.core" -SizeU :: U64 - -ClampTopSizeU :: (val: SizeU, max: SizeU): SizeU - if val > max - return max - return val - -Getalignoffset :: (size: SizeU, align: SizeU): SizeU - mask := align - 1 - val := size & mask - if val != 0 - val = align - val - return val - -AlignUp :: (size: SizeU, align: SizeU): SizeU - result := size + Getalignoffset(size, align) - return result - -ZeroMemory :: (p: *void, size: SizeU) - pcast := p->*U8 - for i := 0->SizeU, i < size, i++ - pcast[i] = 0 - -// -// Unicode -// -QuestionMark16 :: 0x003f -String32 :: struct;; str: *U32; len: int -String16 :: struct;; str: *U16; len: int - -Utf8ToUtf32 :: (c: *U8, max_advance: int): Two(U32, int) - out_str: U32 - advance: int - if (c[0] & 0b10000000) == 0 - if max_advance >= 1 - c0 := c[0]->U32 - out_str = c0 - advance = 1 - - elif (c[0] & 0b11100000) == 0b11000000 - if (c[1] & 0b11000000) == 0b10000000 // Continuation byte required - if max_advance >= 2 - c0 := c[0]->U32; c1 := c[1]->U32 - out_str = (c0 & 0b00011111) << 6 | (c1 & 0b00111111) - advance = 2 - - elif (c[0] & 0b11110000) == 0b11100000 - if (c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000 // Two continuation bytes required - if max_advance >= 3 - c0 := c[0]->U32; c1 := c[1]->U32; c2 := c[2]->U32 - out_str = (c0 & 0b00001111) << 12 | (c1 & 0b00111111) << 6 | (c2 & 0b00111111) - advance = 3 - - elif (c[0] & 0b11111000) == 0b11110000 - if (c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000 && (c[3] & 0b11000000) == 0b10000000 // Three continuation bytes required - if max_advance >= 4 - c0 := c[0]->U32; c1 := c[1]->U32; c2 := c[2]->U32; c3 := c[3]->U32 - out_str = (c0 & 0b00001111) << 18 | (c1 & 0b00111111) << 12 | (c2 & 0b00111111) << 6 | (c3 & 0b00111111) - advance = 4 - - return {out_str, advance} - -Utf32ToUtf16 :: (codepoint: U32): Two([2]U16, int) - result: Two([2]U16, int) - if codepoint < 0x10000 - result.a[0] = codepoint->U16 - result.b = 1 - elif codepoint <= 0x10FFFF - code: U32 = (codepoint - 0x10000) - result.a[0] = (0xD800 | (code >> 10))->U16 - result.a[1] = (0xDC00 | (code & 0x3FF))->U16 - result.b = 2 - return result - -StringToString16 :: (arena: *Arena, in: String): String16 - in_str := &in[0] - // @Note(Krzosa): Should be more then enough space - alloc_size := (Len(in)*2)+1 - result := String16{str = PushSize(arena, alloc_size->U64)} - for i := 0, i < Len(in) - a := Utf8ToUtf32(in_str + i, Len(in) - i) - s32 := a.a - s32_len := a.b - if s32_len != 0 - i += s32_len - s16, s16_len := Utf32ToUtf16(s32) - if s16_len != 0 - for j := 0, j < s16_len, j++ - result.str[result.len++] = s16[j] - else - result.str[result.len++] = QuestionMark16 - break - else - result.str[result.len++] = QuestionMark16 - break - - result.str[result.len] = 0 - return result - -CStringCompare :: (a: *char, b: *char): bool - i := 0 - for , a[i] != 0, i+=1 - if a[i] != b[i] - return false - if a[i] != b[i] - return false - return true - - -TestUnicode :: (arena: *Arena) - string := " 豈 更 車 賈 滑 串 句 龜 龜 契 金 喇 奈 懶 癩 羅 蘿 螺 裸 邏 樂 洛 烙 珞 落 酪 駱 亂 卵 欄 爛 蘭 鸞 嵐 濫 藍 襤 拉 臘 蠟 廊 朗 浪 狼 郎 來 冷 勞 擄 櫓 爐 盧 老 蘆 虜 路 露 魯 鷺 碌 祿 綠 菉 錄 鹿 論 壟 弄 籠 聾 牢 磊 賂 雷 壘 屢 樓 淚 漏 累 縷 陋 勒 肋 凜 凌 稜 綾 菱 陵 讀 拏 樂 諾 丹 寧 怒 率 異 北 磻 便 復 不 泌 數 索 參 塞 省 葉 說 殺 辰 沈 拾 若 掠 略 亮 兩 凉 梁 糧 良 諒 量 勵 ..." - string_result := StringToString16(arena, string) - print(string_result) - - s32, s32_len := Utf8ToUtf32('A', 1) - assert(s32 == 'A', "Invalid decode") - - s32_2, s32_len_2 := Utf8ToUtf32('ć', 2) - assert(s32_2 == 0x107, "Invalid decode") - - s32_3, s32_len_3 := Utf8ToUtf32('ó', 2) - assert(s32_3 == 0xF3, "Invalid decode") +#import "Arena.core" +OS :: #import "OS$OS.core" +SizeU :: U64 + +ClampTopSizeU :: (val: SizeU, max: SizeU): SizeU + if val > max + return max + return val + +Getalignoffset :: (size: SizeU, align: SizeU): SizeU + mask := align - 1 + val := size & mask + if val != 0 + val = align - val + return val + +AlignUp :: (size: SizeU, align: SizeU): SizeU + result := size + Getalignoffset(size, align) + return result + +ZeroMemory :: (p: *void, size: SizeU) + pcast := p->*U8 + for i := 0->SizeU, i < size, i++ + pcast[i] = 0 + +// +// Unicode +// +QuestionMark16 :: 0x003f +String32 :: struct;; str: *U32; len: int +String16 :: struct;; str: *U16; len: int + +Utf8ToUtf32 :: (c: *U8, max_advance: int): Two(U32, int) + out_str: U32 + advance: int + if (c[0] & 0b10000000) == 0 + if max_advance >= 1 + c0 := c[0]->U32 + out_str = c0 + advance = 1 + + elif (c[0] & 0b11100000) == 0b11000000 + if (c[1] & 0b11000000) == 0b10000000 // Continuation byte required + if max_advance >= 2 + c0 := c[0]->U32; c1 := c[1]->U32 + out_str = (c0 & 0b00011111) << 6 | (c1 & 0b00111111) + advance = 2 + + elif (c[0] & 0b11110000) == 0b11100000 + if (c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000 // Two continuation bytes required + if max_advance >= 3 + c0 := c[0]->U32; c1 := c[1]->U32; c2 := c[2]->U32 + out_str = (c0 & 0b00001111) << 12 | (c1 & 0b00111111) << 6 | (c2 & 0b00111111) + advance = 3 + + elif (c[0] & 0b11111000) == 0b11110000 + if (c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000 && (c[3] & 0b11000000) == 0b10000000 // Three continuation bytes required + if max_advance >= 4 + c0 := c[0]->U32; c1 := c[1]->U32; c2 := c[2]->U32; c3 := c[3]->U32 + out_str = (c0 & 0b00001111) << 18 | (c1 & 0b00111111) << 12 | (c2 & 0b00111111) << 6 | (c3 & 0b00111111) + advance = 4 + + return {out_str, advance} + +Utf32ToUtf16 :: (codepoint: U32): Two([2]U16, int) + result: Two([2]U16, int) + if codepoint < 0x10000 + result.a[0] = codepoint->U16 + result.b = 1 + elif codepoint <= 0x10FFFF + code: U32 = (codepoint - 0x10000) + result.a[0] = (0xD800 | (code >> 10))->U16 + result.a[1] = (0xDC00 | (code & 0x3FF))->U16 + result.b = 2 + return result + +StringToString16 :: (arena: *Arena, in: String): String16 + in_str := &in[0] + // @Note(Krzosa): Should be more then enough space + alloc_size := (Len(in)*2)+1 + result := String16{str = PushSize(arena, alloc_size->U64)} + for i := 0, i < Len(in) + a := Utf8ToUtf32(in_str + i, Len(in) - i) + s32 := a.a + s32_len := a.b + if s32_len != 0 + i += s32_len + s16, s16_len := Utf32ToUtf16(s32) + if s16_len != 0 + for j := 0, j < s16_len, j++ + result.str[result.len++] = s16[j] + else + result.str[result.len++] = QuestionMark16 + break + else + result.str[result.len++] = QuestionMark16 + break + + result.str[result.len] = 0 + return result + +CStringCompare :: (a: *char, b: *char): bool + i := 0 + for , a[i] != 0, i+=1 + if a[i] != b[i] + return false + if a[i] != b[i] + return false + return true + + +TestUnicode :: (arena: *Arena) + string := " 豈 更 車 賈 滑 串 句 龜 龜 契 金 喇 奈 懶 癩 羅 蘿 螺 裸 邏 樂 洛 烙 珞 落 酪 駱 亂 卵 欄 爛 蘭 鸞 嵐 濫 藍 襤 拉 臘 蠟 廊 朗 浪 狼 郎 來 冷 勞 擄 櫓 爐 盧 老 蘆 虜 路 露 魯 鷺 碌 祿 綠 菉 錄 鹿 論 壟 弄 籠 聾 牢 磊 賂 雷 壘 屢 樓 淚 漏 累 縷 陋 勒 肋 凜 凌 稜 綾 菱 陵 讀 拏 樂 諾 丹 寧 怒 率 異 北 磻 便 復 不 泌 數 索 參 塞 省 葉 說 殺 辰 沈 拾 若 掠 略 亮 兩 凉 梁 糧 良 諒 量 勵 ..." + string_result := StringToString16(arena, string) + print(string_result) + + s32, s32_len := Utf8ToUtf32('A', 1) + assert(s32 == 'A', "Invalid decode") + + s32_2, s32_len_2 := Utf8ToUtf32('ć', 2) + assert(s32_2 == 0x107, "Invalid decode") + + s32_3, s32_len_3 := Utf8ToUtf32('ó', 2) + assert(s32_3 == 0xF3, "Invalid decode") diff --git a/build/modules/GDI32.core b/modules/GDI32.core similarity index 98% rename from build/modules/GDI32.core rename to modules/GDI32.core index 1b3aada..5a72cda 100644 --- a/build/modules/GDI32.core +++ b/modules/GDI32.core @@ -1,41 +1,41 @@ -#import "KERNEL32.core" -#link "gdi32" -RBGQUAD :: struct;; rgbBlue: BYTE; rgbGreen: BYTE; rgbRed: BYTE; rgbReserved: BYTE -BITMAPINFOHEADER :: struct;; biSize: DWORD; biWidth: LONG; biHeight: LONG; biPlanes: WORD; biBitCount: WORD; biCompression: DWORD; biSizeImage: DWORD; biXPelsPerMeter: LONG; biYPelsPerMeter: LONG; biClrUsed: DWORD; biClrImportant: DWORD -BITMAPINFO :: struct;; bmiHeader: BITMAPINFOHEADER; bmiColors: [1]RBGQUAD -HGDIOBJ :: HANDLE - -BI_RGB :: 0x0000 -BI_RLE8 :: 0x0001 -BI_RLE4 :: 0x0002 -BI_BITFIELDS :: 0x0003 -BI_JPEG :: 0x0004 -BI_PNG :: 0x0005 -BI_CMYK :: 0x000B -BI_CMYKRLE8 :: 0x000C -BI_CMYKRLE4 :: 0x000 -DIB_RGB_COLORS :: 0x00 - -SRCCOPY :: 0x00CC0020 /* dest = source */ -SRCPAINT :: 0x00EE0086 /* dest = source OR dest */ -SRCAND :: 0x008800C6 /* dest = source AND dest */ -SRCINVERT :: 0x00660046 /* dest = source XOR dest */ -SRCERASE :: 0x00440328 /* dest = source AND (NOT dest ) */ -NOTSRCCOPY :: 0x00330008 /* dest = (NOT source) */ -NOTSRCERASE :: 0x001100A6 /* dest = (NOT src) AND (NOT dest) */ -MERGECOPY :: 0x00C000CA /* dest = (source AND pattern) */ -MERGEPAINT :: 0x00BB0226 /* dest = (NOT source) OR dest */ -PATCOPY :: 0x00F00021 /* dest = pattern */ -PATPAINT :: 0x00FB0A09 /* dest = DPSnoo */ -PATINVERT :: 0x005A0049 /* dest = pattern XOR dest */ -DSTINVERT :: 0x00550009 /* dest = (NOT dest) */ -BLACKNESS :: 0x00000042 /* dest = BLACK */ -WHITENESS :: 0x00FF0062 /* dest = WHITE */ - - -CreateDIBSection :: #foreign (hdc: HDC, pbmi: *BITMAPINFO, usage: UINT, ppvBits: **VOID, hSection: HANDLE, offset: DWORD): HBITMAP -CreateCompatibleDC :: #foreign (hdc: HDC): HDC -SelectObject :: #foreign (hdc: HDC, h: HGDIOBJ): HGDIOBJ -BitBlt :: #foreign (hdc: HDC, x: int, y: int, cx: int, cy: int, hdcSrc: HDC, x1: int, y1: int, ro: DWORD): BOOL -DeleteDC :: #foreign (hdc: HDC): BOOL +#import "KERNEL32.core" +#link "gdi32" +RBGQUAD :: struct;; rgbBlue: BYTE; rgbGreen: BYTE; rgbRed: BYTE; rgbReserved: BYTE +BITMAPINFOHEADER :: struct;; biSize: DWORD; biWidth: LONG; biHeight: LONG; biPlanes: WORD; biBitCount: WORD; biCompression: DWORD; biSizeImage: DWORD; biXPelsPerMeter: LONG; biYPelsPerMeter: LONG; biClrUsed: DWORD; biClrImportant: DWORD +BITMAPINFO :: struct;; bmiHeader: BITMAPINFOHEADER; bmiColors: [1]RBGQUAD +HGDIOBJ :: HANDLE + +BI_RGB :: 0x0000 +BI_RLE8 :: 0x0001 +BI_RLE4 :: 0x0002 +BI_BITFIELDS :: 0x0003 +BI_JPEG :: 0x0004 +BI_PNG :: 0x0005 +BI_CMYK :: 0x000B +BI_CMYKRLE8 :: 0x000C +BI_CMYKRLE4 :: 0x000 +DIB_RGB_COLORS :: 0x00 + +SRCCOPY :: 0x00CC0020 /* dest = source */ +SRCPAINT :: 0x00EE0086 /* dest = source OR dest */ +SRCAND :: 0x008800C6 /* dest = source AND dest */ +SRCINVERT :: 0x00660046 /* dest = source XOR dest */ +SRCERASE :: 0x00440328 /* dest = source AND (NOT dest ) */ +NOTSRCCOPY :: 0x00330008 /* dest = (NOT source) */ +NOTSRCERASE :: 0x001100A6 /* dest = (NOT src) AND (NOT dest) */ +MERGECOPY :: 0x00C000CA /* dest = (source AND pattern) */ +MERGEPAINT :: 0x00BB0226 /* dest = (NOT source) OR dest */ +PATCOPY :: 0x00F00021 /* dest = pattern */ +PATPAINT :: 0x00FB0A09 /* dest = DPSnoo */ +PATINVERT :: 0x005A0049 /* dest = pattern XOR dest */ +DSTINVERT :: 0x00550009 /* dest = (NOT dest) */ +BLACKNESS :: 0x00000042 /* dest = BLACK */ +WHITENESS :: 0x00FF0062 /* dest = WHITE */ + + +CreateDIBSection :: #foreign (hdc: HDC, pbmi: *BITMAPINFO, usage: UINT, ppvBits: **VOID, hSection: HANDLE, offset: DWORD): HBITMAP +CreateCompatibleDC :: #foreign (hdc: HDC): HDC +SelectObject :: #foreign (hdc: HDC, h: HGDIOBJ): HGDIOBJ +BitBlt :: #foreign (hdc: HDC, x: int, y: int, cx: int, cy: int, hdcSrc: HDC, x1: int, y1: int, ro: DWORD): BOOL +DeleteDC :: #foreign (hdc: HDC): BOOL DeleteObject :: #foreign (ho : HGDIOBJ): BOOL \ No newline at end of file diff --git a/build/modules/KERNEL32.core b/modules/KERNEL32.core similarity index 97% rename from build/modules/KERNEL32.core rename to modules/KERNEL32.core index 62571d6..92fa1ea 100644 --- a/build/modules/KERNEL32.core +++ b/modules/KERNEL32.core @@ -1,130 +1,130 @@ -#link "kernel32" - -DWORD :: U32 -LPCSTR :: *char -LPSTR :: *char -LPCWSTR :: *U16 -HWND :: *void -HMENU :: *void -HINSTANCE :: *void -HBITMAP :: *void -HDC :: *void -LPVOID :: *void -SIZE_T :: U64 -BOOL :: int -HMODULE :: HANDLE -HANDLE :: *void -VOID :: void -HICON :: HANDLE -HCURSOR :: HANDLE -HBRUSH :: HANDLE -LPDWORD :: *DWORD -LRESULT :: S64 -WPARAM :: U64 -LPARAM :: S64 -BYTE :: uchar -WORD :: short -LONG :: long -UINT :: uint -ATOM :: WORD -LARGE_INTEGER :: S64 -PLARGE_INTEGER :: *LARGE_INTEGER -LPOVERLAPPED :: *OVERLAPPED - -LONG_PTR :: *S64 -ULONG_PTR :: *U64 - -MEM_COMMIT :: 0x00001000 -MEM_RESERVE :: 0x00002000 -MEM_RESET :: 0x00080000 -MEM_RESET_UNDO :: 0x1000000 -MEM_DECOMMIT :: 0x00004000 -MEM_RELEASE :: 0x00008000 - -PAGE_NOACCESS :: 1 -PAGE_READONLY :: 2 -PAGE_READWRITE :: 4 -PAGE_WRITECOPY :: 8 -PAGE_EXECUTE :: 0x10; PAGE_EXECUTE_READ :: 0x20; PAGE_EXECUTE_READWRITE :: 0x40; PAGE_EXECUTE_WRITECOPY :: 0x80 -VirtualAlloc :: #foreign (lpAddress: LPVOID, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD): LPVOID -VirtualFree :: #foreign (lpAddress: LPVOID, dwSize: SIZE_T, dwFreeType: DWORD): BOOL - -HEAP_ZERO_MEMORY :: 0x8; HEAP_NO_SERIALIZE :: 0x1; HEAP_GENERATE_EXCEPTIONS :: 0x4 -GetProcessHeap :: #foreign (): HANDLE -HeapAlloc :: #foreign (hHeap: HANDLE, dwFlags: DWORD, dwByte: SIZE_T): LPVOID -HeapFree :: #foreign (hHeap: HANDLE, dwFlags: DWORD, lpMe: LPVOID): BOOL - -STD_INPUT_HANDLE :: 4294967286//(-10)->DWORD -STD_OUTPUT_HANDLE :: 4294967285//(-11)->DWORD -//STD_ERROR_HANDLE :: (-12)->DWORD -GetStdHandle :: #foreign (nStdHandle: DWORD): HANDLE -WriteConsoleA :: #foreign (hConsoleOutput: HANDLE,lpBuffer: *VOID,nNumberOfCharsToWrite: DWORD,lpNumberOfCharsWritten: LPDWORD,lpReserve: LPVOID): BOOL -WriteConsoleW :: #foreign (hConsoleOutput: HANDLE,lpBuffer: *VOID,nNumberOfCharsToWrite: DWORD,lpNumberOfCharsWritten: LPDWORD,lpReserve: LPVOID): BOOL -__debugbreak :: #foreign () - -GetModuleHandleA :: #foreign (lpModuleName: LPCSTR): HMODULE -ExitProcess :: #foreign (uExitCode: UINT) -GetLastError :: #foreign (): DWORD -QueryPerformanceFrequency :: #foreign (lpFrequency: *LARGE_INTEGER): BOOL -QueryPerformanceCounter :: #foreign (lpFrequency: *LARGE_INTEGER): BOOL -Sleep :: #foreign (dwMilliseconds: DWORD) -OutputDebugStringA :: #foreign (lpOutputString: LPCSTR) - -CreateFileW :: #foreign (lpFileName: LPCWSTR, dwDesiredAccess: DWORD, dwShareMode: DWORD, lpSecurityAttributes: LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: HANDLE): HANDLE -ReadFile :: #foreign (hFile: HANDLE, lpBuffer: LPVOID, nNumberOfBytesToRead: DWORD, lpNumberOfBytesRead: LPDWORD, lpOverlapped: LPOVERLAPPED): BOOL -CloseHandle :: #foreign (hObject: HANDLE): BOOL -GetFileSizeEx :: #foreign (hFile: HANDLE, lpFileSize: PLARGE_INTEGER) - -OVERLAPPED :: struct - Internal: ULONG_PTR - InternalHigh: ULONG_PTR - Pointer: PVOID - hEvent: HANDLE - -LPSECURITY_ATTRIBUTES :: *SECURITY_ATTRIBUTES -SECURITY_ATTRIBUTES :: struct - nLength: DWORD - lpSecurityDescriptor: LPVOID - bInheritHandle: BOOL - -GENERIC_READ :: 0x80000000 -GENERIC_WRITE :: 0x40000000 -GENERIC_EXECUTE :: 0x20000000 -GENERIC_ALL :: 0x10000000 - -CREATE_NEW :: 1 -CREATE_ALWAYS :: 2 -OPEN_EXISTING :: 3 -OPEN_ALWAYS :: 4 -TRUNCATE_EXISTING :: 5 - -FILE_SHARE_READ :: 0x00000001 -FILE_SHARE_WRITE :: 0x00000002 -FILE_SHARE_DELETE :: 0x00000004 - -// INVALID_HANDLE_VALUE :: ((-1)->LONG_PTR)->HANDLE -INVALID_HANDLE_VALUE :: (~(0->U64)) - - -FILE_ATTRIBUTE_READONLY :: 0x00000001 -FILE_ATTRIBUTE_HIDDEN :: 0x00000002 -FILE_ATTRIBUTE_SYSTEM :: 0x00000004 -FILE_ATTRIBUTE_DIRECTORY :: 0x00000010 -FILE_ATTRIBUTE_ARCHIVE :: 0x00000020 -FILE_ATTRIBUTE_DEVICE :: 0x00000040 -FILE_ATTRIBUTE_NORMAL :: 0x00000080 -FILE_ATTRIBUTE_TEMPORARY :: 0x00000100 -FILE_ATTRIBUTE_SPARSE_FILE :: 0x00000200 -FILE_ATTRIBUTE_REPARSE_POINT :: 0x00000400 -FILE_ATTRIBUTE_COMPRESSED :: 0x00000800 -FILE_ATTRIBUTE_OFFLINE :: 0x00001000 -FILE_ATTRIBUTE_NOT_CONTENT_INDEXED :: 0x00002000 -FILE_ATTRIBUTE_ENCRYPTED :: 0x00004000 -FILE_ATTRIBUTE_INTEGRITY_STREAM :: 0x00008000 -FILE_ATTRIBUTE_VIRTUAL :: 0x00010000 -FILE_ATTRIBUTE_NO_SCRUB_DATA :: 0x00020000 -FILE_ATTRIBUTE_EA :: 0x00040000 -FILE_ATTRIBUTE_PINNED :: 0x00080000 -FILE_ATTRIBUTE_UNPINNED :: 0x00100000 -FILE_ATTRIBUTE_RECALL_ON_OPEN :: 0x00040000 +#link "kernel32" + +DWORD :: U32 +LPCSTR :: *char +LPSTR :: *char +LPCWSTR :: *U16 +HWND :: *void +HMENU :: *void +HINSTANCE :: *void +HBITMAP :: *void +HDC :: *void +LPVOID :: *void +SIZE_T :: U64 +BOOL :: int +HMODULE :: HANDLE +HANDLE :: *void +VOID :: void +HICON :: HANDLE +HCURSOR :: HANDLE +HBRUSH :: HANDLE +LPDWORD :: *DWORD +LRESULT :: S64 +WPARAM :: U64 +LPARAM :: S64 +BYTE :: uchar +WORD :: short +LONG :: long +UINT :: uint +ATOM :: WORD +LARGE_INTEGER :: S64 +PLARGE_INTEGER :: *LARGE_INTEGER +LPOVERLAPPED :: *OVERLAPPED + +LONG_PTR :: *S64 +ULONG_PTR :: *U64 + +MEM_COMMIT :: 0x00001000 +MEM_RESERVE :: 0x00002000 +MEM_RESET :: 0x00080000 +MEM_RESET_UNDO :: 0x1000000 +MEM_DECOMMIT :: 0x00004000 +MEM_RELEASE :: 0x00008000 + +PAGE_NOACCESS :: 1 +PAGE_READONLY :: 2 +PAGE_READWRITE :: 4 +PAGE_WRITECOPY :: 8 +PAGE_EXECUTE :: 0x10; PAGE_EXECUTE_READ :: 0x20; PAGE_EXECUTE_READWRITE :: 0x40; PAGE_EXECUTE_WRITECOPY :: 0x80 +VirtualAlloc :: #foreign (lpAddress: LPVOID, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD): LPVOID +VirtualFree :: #foreign (lpAddress: LPVOID, dwSize: SIZE_T, dwFreeType: DWORD): BOOL + +HEAP_ZERO_MEMORY :: 0x8; HEAP_NO_SERIALIZE :: 0x1; HEAP_GENERATE_EXCEPTIONS :: 0x4 +GetProcessHeap :: #foreign (): HANDLE +HeapAlloc :: #foreign (hHeap: HANDLE, dwFlags: DWORD, dwByte: SIZE_T): LPVOID +HeapFree :: #foreign (hHeap: HANDLE, dwFlags: DWORD, lpMe: LPVOID): BOOL + +STD_INPUT_HANDLE :: 4294967286//(-10)->DWORD +STD_OUTPUT_HANDLE :: 4294967285//(-11)->DWORD +//STD_ERROR_HANDLE :: (-12)->DWORD +GetStdHandle :: #foreign (nStdHandle: DWORD): HANDLE +WriteConsoleA :: #foreign (hConsoleOutput: HANDLE,lpBuffer: *VOID,nNumberOfCharsToWrite: DWORD,lpNumberOfCharsWritten: LPDWORD,lpReserve: LPVOID): BOOL +WriteConsoleW :: #foreign (hConsoleOutput: HANDLE,lpBuffer: *VOID,nNumberOfCharsToWrite: DWORD,lpNumberOfCharsWritten: LPDWORD,lpReserve: LPVOID): BOOL +__debugbreak :: #foreign () + +GetModuleHandleA :: #foreign (lpModuleName: LPCSTR): HMODULE +ExitProcess :: #foreign (uExitCode: UINT) +GetLastError :: #foreign (): DWORD +QueryPerformanceFrequency :: #foreign (lpFrequency: *LARGE_INTEGER): BOOL +QueryPerformanceCounter :: #foreign (lpFrequency: *LARGE_INTEGER): BOOL +Sleep :: #foreign (dwMilliseconds: DWORD) +OutputDebugStringA :: #foreign (lpOutputString: LPCSTR) + +CreateFileW :: #foreign (lpFileName: LPCWSTR, dwDesiredAccess: DWORD, dwShareMode: DWORD, lpSecurityAttributes: LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: HANDLE): HANDLE +ReadFile :: #foreign (hFile: HANDLE, lpBuffer: LPVOID, nNumberOfBytesToRead: DWORD, lpNumberOfBytesRead: LPDWORD, lpOverlapped: LPOVERLAPPED): BOOL +CloseHandle :: #foreign (hObject: HANDLE): BOOL +GetFileSizeEx :: #foreign (hFile: HANDLE, lpFileSize: PLARGE_INTEGER) + +OVERLAPPED :: struct + Internal: ULONG_PTR + InternalHigh: ULONG_PTR + Pointer: PVOID + hEvent: HANDLE + +LPSECURITY_ATTRIBUTES :: *SECURITY_ATTRIBUTES +SECURITY_ATTRIBUTES :: struct + nLength: DWORD + lpSecurityDescriptor: LPVOID + bInheritHandle: BOOL + +GENERIC_READ :: 0x80000000 +GENERIC_WRITE :: 0x40000000 +GENERIC_EXECUTE :: 0x20000000 +GENERIC_ALL :: 0x10000000 + +CREATE_NEW :: 1 +CREATE_ALWAYS :: 2 +OPEN_EXISTING :: 3 +OPEN_ALWAYS :: 4 +TRUNCATE_EXISTING :: 5 + +FILE_SHARE_READ :: 0x00000001 +FILE_SHARE_WRITE :: 0x00000002 +FILE_SHARE_DELETE :: 0x00000004 + +// INVALID_HANDLE_VALUE :: ((-1)->LONG_PTR)->HANDLE +INVALID_HANDLE_VALUE :: (~(0->U64)) + + +FILE_ATTRIBUTE_READONLY :: 0x00000001 +FILE_ATTRIBUTE_HIDDEN :: 0x00000002 +FILE_ATTRIBUTE_SYSTEM :: 0x00000004 +FILE_ATTRIBUTE_DIRECTORY :: 0x00000010 +FILE_ATTRIBUTE_ARCHIVE :: 0x00000020 +FILE_ATTRIBUTE_DEVICE :: 0x00000040 +FILE_ATTRIBUTE_NORMAL :: 0x00000080 +FILE_ATTRIBUTE_TEMPORARY :: 0x00000100 +FILE_ATTRIBUTE_SPARSE_FILE :: 0x00000200 +FILE_ATTRIBUTE_REPARSE_POINT :: 0x00000400 +FILE_ATTRIBUTE_COMPRESSED :: 0x00000800 +FILE_ATTRIBUTE_OFFLINE :: 0x00001000 +FILE_ATTRIBUTE_NOT_CONTENT_INDEXED :: 0x00002000 +FILE_ATTRIBUTE_ENCRYPTED :: 0x00004000 +FILE_ATTRIBUTE_INTEGRITY_STREAM :: 0x00008000 +FILE_ATTRIBUTE_VIRTUAL :: 0x00010000 +FILE_ATTRIBUTE_NO_SCRUB_DATA :: 0x00020000 +FILE_ATTRIBUTE_EA :: 0x00040000 +FILE_ATTRIBUTE_PINNED :: 0x00080000 +FILE_ATTRIBUTE_UNPINNED :: 0x00100000 +FILE_ATTRIBUTE_RECALL_ON_OPEN :: 0x00040000 FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS :: 0x00400000 \ No newline at end of file diff --git a/build/modules/Language.core b/modules/Language.core similarity index 93% rename from build/modules/Language.core rename to modules/Language.core index 849ef4f..727c5d4 100644 --- a/build/modules/Language.core +++ b/modules/Language.core @@ -1,60 +1,60 @@ -/* -Any :: struct - data: *void - type: Type - -Type_Info_Kind :: enum - S64 // FIRST_NUMERIC - S32 - S16 - S8 - INT - CHAR - U64 - U32 - U16 - U8 - F32 - F64 - POINTER - BOOL // LAST_NUMERIC - STRING - VOID - ARRAY - LAMBDA - STRUCT - UNION - ENUM - TYPE - SLICE - TUPLE - -Type_Info_Struct_Member :: struct - name: String - type: Type - offset: S64 - -Type_Info :: struct - kind: Type_Info_Kind - size: S64 - align: S64 - is_unsigned: bool - type: Type - - base_type: Type - array_size: S64 - struct_members: *Type_Info_Struct_Member - struct_member_count: S64 - lambda_arguments: *Type_Info - lambda_argument_count: S64 - lambda_return: Type - -type_infos_len: S64 #foreign -type_infos : *Type_Info #foreign - -GetTypeInfo :: (type: Type): *Type_Info - id := type->S64 - if id >= type_infos_len - return 0 - return type_infos + id +/* +Any :: struct + data: *void + type: Type + +Type_Info_Kind :: enum + S64 // FIRST_NUMERIC + S32 + S16 + S8 + INT + CHAR + U64 + U32 + U16 + U8 + F32 + F64 + POINTER + BOOL // LAST_NUMERIC + STRING + VOID + ARRAY + LAMBDA + STRUCT + UNION + ENUM + TYPE + SLICE + TUPLE + +Type_Info_Struct_Member :: struct + name: String + type: Type + offset: S64 + +Type_Info :: struct + kind: Type_Info_Kind + size: S64 + align: S64 + is_unsigned: bool + type: Type + + base_type: Type + array_size: S64 + struct_members: *Type_Info_Struct_Member + struct_member_count: S64 + lambda_arguments: *Type_Info + lambda_argument_count: S64 + lambda_return: Type + +type_infos_len: S64 #foreign +type_infos : *Type_Info #foreign + +GetTypeInfo :: (type: Type): *Type_Info + id := type->S64 + if id >= type_infos_len + return 0 + return type_infos + id */ \ No newline at end of file diff --git a/build/modules/LibC.core b/modules/LibC.core similarity index 97% rename from build/modules/LibC.core rename to modules/LibC.core index 3a3a7b0..c33a528 100644 --- a/build/modules/LibC.core +++ b/modules/LibC.core @@ -1,20 +1,20 @@ -size_t :: U64 // @todo(Krzosa): Need this type - -malloc :: #foreign (size: size_t): *void -realloc :: #foreign (ptr: *void, size: size_t): *void -free :: #foreign (ptr: *void) - -memset :: #foreign (ptr: *void, val: int, num: size_t): *void -memcpy :: #foreign (dst: *void, src: *void, size: size_t): *void -memmove :: #foreign (dst: *void, src: *void, size: size_t): *void - -FILE :: #strict U64 // Doesnt matter the type just handle -fopen :: #foreign (file: *char, mode: *char): *FILE -fclose :: #foreign (file: *FILE): int -fseek :: #foreign (public_stream: *FILE, offset: long, whence: int): int -ftell :: #foreign (public_stream: *FILE): long -fread :: #foreign (buffer: *void, element_size: size_t, element_count: size_t, stream: *FILE): size_t - -SEEK_CUR :: 1 -SEEK_END :: 2 -SEEK_SET :: 0 +size_t :: U64 // @todo(Krzosa): Need this type + +malloc :: #foreign (size: size_t): *void +realloc :: #foreign (ptr: *void, size: size_t): *void +free :: #foreign (ptr: *void) + +memset :: #foreign (ptr: *void, val: int, num: size_t): *void +memcpy :: #foreign (dst: *void, src: *void, size: size_t): *void +memmove :: #foreign (dst: *void, src: *void, size: size_t): *void + +FILE :: #strict U64 // Doesnt matter the type just handle +fopen :: #foreign (file: *char, mode: *char): *FILE +fclose :: #foreign (file: *FILE): int +fseek :: #foreign (public_stream: *FILE, offset: long, whence: int): int +ftell :: #foreign (public_stream: *FILE): long +fread :: #foreign (buffer: *void, element_size: size_t, element_count: size_t, stream: *FILE): size_t + +SEEK_CUR :: 1 +SEEK_END :: 2 +SEEK_SET :: 0 diff --git a/build/modules/MathF32.core b/modules/MathF32.core similarity index 95% rename from build/modules/MathF32.core rename to modules/MathF32.core index ad3c075..ce03ede 100644 --- a/build/modules/MathF32.core +++ b/modules/MathF32.core @@ -1,34 +1,34 @@ -sqrtf :: #foreign (value: F32): F32 -cosf :: #foreign (value: F32): F32 -sinf :: #foreign (value: F32): F32 -floorf :: #foreign (value: F32): F32 -roundf :: #foreign (value: F32): F32 -ceilf :: #foreign (value: F32): F32 - -Floor :: floorf -Round :: roundf -Ceil :: ceilf -SquareRoot :: sqrtf -Cos :: cosf -Sin :: sinf - -Clamp :: (min: F32, value: F32, max: F32): F32 - if value > max;; return max - if value < min;; return min - return value - -ClampBottom :: (min: F32, value: F32): F32 - if value < min;; return min - return value - -Absolute :: (val: F32): F32 - if val < 0;; return -val - return val - -Min :: (a: F32, b: F32): F32 - if a > b ;; return b - return a - -Max :: (a: F32, b: F32): F32 - if a > b ;; return a - return b +sqrtf :: #foreign (value: F32): F32 +cosf :: #foreign (value: F32): F32 +sinf :: #foreign (value: F32): F32 +floorf :: #foreign (value: F32): F32 +roundf :: #foreign (value: F32): F32 +ceilf :: #foreign (value: F32): F32 + +Floor :: floorf +Round :: roundf +Ceil :: ceilf +SquareRoot :: sqrtf +Cos :: cosf +Sin :: sinf + +Clamp :: (min: F32, value: F32, max: F32): F32 + if value > max;; return max + if value < min;; return min + return value + +ClampBottom :: (min: F32, value: F32): F32 + if value < min;; return min + return value + +Absolute :: (val: F32): F32 + if val < 0;; return -val + return val + +Min :: (a: F32, b: F32): F32 + if a > b ;; return b + return a + +Max :: (a: F32, b: F32): F32 + if a > b ;; return a + return b diff --git a/build/modules/MathVec2.core b/modules/MathVec2.core similarity index 98% rename from build/modules/MathVec2.core rename to modules/MathVec2.core index 9c5ae59..90ddbfb 100644 --- a/build/modules/MathVec2.core +++ b/modules/MathVec2.core @@ -1,32 +1,32 @@ - -Vec2I :: struct;; x: int; y: int -Vec2 :: struct;; x: F32; y: F32 - -"*" :: (a: Vec2, b: Vec2): Vec2 ;; return {a.x*b.x, a.y*b.y} -"*" :: (a: Vec2, b: F32) : Vec2 ;; return {a.x*b, a.y*b} -"*" :: (a: F32, b: Vec2) : Vec2 ;; return {a*b.x, a*b.y} -"-" :: (a: Vec2, b: Vec2): Vec2 ;; return {a.x-b.x, a.y-b.y} -"-" :: (a: Vec2, b: F32) : Vec2 ;; return {a.x-b, a.y-b} -"-" :: (a: F32, b: Vec2) : Vec2 ;; return {a-b.x, a-b.y} -"+" :: (a: Vec2, b: Vec2): Vec2 ;; return {a.x+b.x, a.y+b.y} -"+" :: (a: Vec2, b: F32) : Vec2 ;; return {a.x+b, a.y+b} -"+" :: (a: F32, b: Vec2) : Vec2 ;; return {a+b.x, a+b.y} -"/" :: (a: Vec2, b: Vec2): Vec2 ;; return {a.x/b.x, a.y/b.y} -"/" :: (a: Vec2, b: F32) : Vec2 ;; return {a.x/b, a.y/b} -"/" :: (a: F32, b: Vec2) : Vec2 ;; return {a/b.x, a/b.y} - -"*" :: (a: Vec2I, b: Vec2I): Vec2I ;; return {a.x*b.x, a.y*b.y} -"*" :: (a: Vec2I, b: int) : Vec2I ;; return {a.x*b, a.y*b} -"*" :: (a: int, b: Vec2I) : Vec2I ;; return {a*b.x, a*b.y} -"-" :: (a: Vec2I, b: Vec2I): Vec2I ;; return {a.x-b.x, a.y-b.y} -"-" :: (a: Vec2I, b: int) : Vec2I ;; return {a.x-b, a.y-b} -"-" :: (a: int, b: Vec2I) : Vec2I ;; return {a-b.x, a-b.y} -"+" :: (a: Vec2I, b: Vec2I): Vec2I ;; return {a.x+b.x, a.y+b.y} -"+" :: (a: Vec2I, b: int) : Vec2I ;; return {a.x+b, a.y+b} -"+" :: (a: int, b: Vec2I) : Vec2I ;; return {a+b.x, a+b.y} -"/" :: (a: Vec2I, b: Vec2I): Vec2I ;; return {a.x/b.x, a.y/b.y} -"/" :: (a: Vec2I, b: int) : Vec2I ;; return {a.x/b, a.y/b} -"/" :: (a: int, b: Vec2I) : Vec2I ;; return {a/b.x, a/b.y} - -FloorVec2ToVec2I :: (a: Vec2): Vec2I ;; return {floorf(a.x)->int, floorf(a.y)->int} -CastVec2ToVec2I :: (a: Vec2): Vec2I ;; return {a.x->int, a.y->int} + +Vec2I :: struct;; x: int; y: int +Vec2 :: struct;; x: F32; y: F32 + +"*" :: (a: Vec2, b: Vec2): Vec2 ;; return {a.x*b.x, a.y*b.y} +"*" :: (a: Vec2, b: F32) : Vec2 ;; return {a.x*b, a.y*b} +"*" :: (a: F32, b: Vec2) : Vec2 ;; return {a*b.x, a*b.y} +"-" :: (a: Vec2, b: Vec2): Vec2 ;; return {a.x-b.x, a.y-b.y} +"-" :: (a: Vec2, b: F32) : Vec2 ;; return {a.x-b, a.y-b} +"-" :: (a: F32, b: Vec2) : Vec2 ;; return {a-b.x, a-b.y} +"+" :: (a: Vec2, b: Vec2): Vec2 ;; return {a.x+b.x, a.y+b.y} +"+" :: (a: Vec2, b: F32) : Vec2 ;; return {a.x+b, a.y+b} +"+" :: (a: F32, b: Vec2) : Vec2 ;; return {a+b.x, a+b.y} +"/" :: (a: Vec2, b: Vec2): Vec2 ;; return {a.x/b.x, a.y/b.y} +"/" :: (a: Vec2, b: F32) : Vec2 ;; return {a.x/b, a.y/b} +"/" :: (a: F32, b: Vec2) : Vec2 ;; return {a/b.x, a/b.y} + +"*" :: (a: Vec2I, b: Vec2I): Vec2I ;; return {a.x*b.x, a.y*b.y} +"*" :: (a: Vec2I, b: int) : Vec2I ;; return {a.x*b, a.y*b} +"*" :: (a: int, b: Vec2I) : Vec2I ;; return {a*b.x, a*b.y} +"-" :: (a: Vec2I, b: Vec2I): Vec2I ;; return {a.x-b.x, a.y-b.y} +"-" :: (a: Vec2I, b: int) : Vec2I ;; return {a.x-b, a.y-b} +"-" :: (a: int, b: Vec2I) : Vec2I ;; return {a-b.x, a-b.y} +"+" :: (a: Vec2I, b: Vec2I): Vec2I ;; return {a.x+b.x, a.y+b.y} +"+" :: (a: Vec2I, b: int) : Vec2I ;; return {a.x+b, a.y+b} +"+" :: (a: int, b: Vec2I) : Vec2I ;; return {a+b.x, a+b.y} +"/" :: (a: Vec2I, b: Vec2I): Vec2I ;; return {a.x/b.x, a.y/b.y} +"/" :: (a: Vec2I, b: int) : Vec2I ;; return {a.x/b, a.y/b} +"/" :: (a: int, b: Vec2I) : Vec2I ;; return {a/b.x, a/b.y} + +FloorVec2ToVec2I :: (a: Vec2): Vec2I ;; return {floorf(a.x)->int, floorf(a.y)->int} +CastVec2ToVec2I :: (a: Vec2): Vec2I ;; return {a.x->int, a.y->int} diff --git a/build/modules/MathVec3.core b/modules/MathVec3.core similarity index 97% rename from build/modules/MathVec3.core rename to modules/MathVec3.core index 9ab9774..5cbfda3 100644 --- a/build/modules/MathVec3.core +++ b/modules/MathVec3.core @@ -1,46 +1,46 @@ -#import "MathF32.core" - -Vec3 :: struct ;; x: F32; y: F32; z: F32 -Length :: (a: Vec3): F32 ;; return sqrtf(a.x*a.x + a.y*a.y + a.z*a.z) -Negate :: (a: Vec3): Vec3 ;; return {-a.x, -a.y, -a.z} -Dot :: (a: Vec3, b: Vec3): F32 ;; return a.x*b.x + a.y*b.y + a.z*b.z -"*" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x*b.x, a.y*b.y, a.z*b.z} -"*" :: (a: Vec3, b: F32) : Vec3 ;; return {a.x*b, a.y*b, a.z*b} -"*" :: (a: F32, b: Vec3) : Vec3 ;; return {a*b.x, a*b.y, a*b.z} -"-" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x-b.x, a.y-b.y, a.z-b.z} -"-" :: (a: Vec3, b: F32) : Vec3 ;; return {a.x-b, a.y-b, a.z-b} -"-" :: (a: F32, b: Vec3) : Vec3 ;; return {a-b.x, a-b.y, a-b.z} -"+" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x+b.x, a.y+b.y, a.z+b.z} -"+" :: (a: Vec3, b: F32) : Vec3 ;; return {a.x+b, a.y+b, a.z+b} -"+" :: (a: F32, b: Vec3) : Vec3 ;; return {a+b.x, a+b.y, a+b.z} -"/" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x/b.x, a.y/b.y, a.z/b.z} -"/" :: (a: Vec3, b: F32) : Vec3 ;; return {a.x/b, a.y/b, a.z/b} -"/" :: (a: F32, b: Vec3) : Vec3 ;; return {a/b.x, a/b.y, a/b.z} - -Cross :: (a: Vec3, b: Vec3): Vec3 - result := Vec3{ - a.y * b.z - a.z * b.y, - a.z * b.x - a.x * b.z, - a.x * b.y - a.y * b.x, - } - return result - -Normalize :: (a: Vec3): Vec3 - length := Length(a) - result := a / length - return result - -Reflect :: (a: Vec3, normal: Vec3): Vec3 - an := Dot(a, normal)*2 - result := a - a * an - return result - -ConvertToARGB :: (a: Vec3): U32 - a.x = Clamp(0, a.x, 1) - a.y = Clamp(0, a.y, 1) - a.z = Clamp(0, a.z, 1) - r := (a.x * 255)->U32 << 16 - g := (a.y * 255)->U32 << 8 - b := (a.z * 255)->U32 << 0 - result := r | g | b - return result +#import "MathF32.core" + +Vec3 :: struct ;; x: F32; y: F32; z: F32 +Length :: (a: Vec3): F32 ;; return sqrtf(a.x*a.x + a.y*a.y + a.z*a.z) +Negate :: (a: Vec3): Vec3 ;; return {-a.x, -a.y, -a.z} +Dot :: (a: Vec3, b: Vec3): F32 ;; return a.x*b.x + a.y*b.y + a.z*b.z +"*" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x*b.x, a.y*b.y, a.z*b.z} +"*" :: (a: Vec3, b: F32) : Vec3 ;; return {a.x*b, a.y*b, a.z*b} +"*" :: (a: F32, b: Vec3) : Vec3 ;; return {a*b.x, a*b.y, a*b.z} +"-" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x-b.x, a.y-b.y, a.z-b.z} +"-" :: (a: Vec3, b: F32) : Vec3 ;; return {a.x-b, a.y-b, a.z-b} +"-" :: (a: F32, b: Vec3) : Vec3 ;; return {a-b.x, a-b.y, a-b.z} +"+" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x+b.x, a.y+b.y, a.z+b.z} +"+" :: (a: Vec3, b: F32) : Vec3 ;; return {a.x+b, a.y+b, a.z+b} +"+" :: (a: F32, b: Vec3) : Vec3 ;; return {a+b.x, a+b.y, a+b.z} +"/" :: (a: Vec3, b: Vec3): Vec3 ;; return {a.x/b.x, a.y/b.y, a.z/b.z} +"/" :: (a: Vec3, b: F32) : Vec3 ;; return {a.x/b, a.y/b, a.z/b} +"/" :: (a: F32, b: Vec3) : Vec3 ;; return {a/b.x, a/b.y, a/b.z} + +Cross :: (a: Vec3, b: Vec3): Vec3 + result := Vec3{ + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x, + } + return result + +Normalize :: (a: Vec3): Vec3 + length := Length(a) + result := a / length + return result + +Reflect :: (a: Vec3, normal: Vec3): Vec3 + an := Dot(a, normal)*2 + result := a - a * an + return result + +ConvertToARGB :: (a: Vec3): U32 + a.x = Clamp(0, a.x, 1) + a.y = Clamp(0, a.y, 1) + a.z = Clamp(0, a.z, 1) + r := (a.x * 255)->U32 << 16 + g := (a.y * 255)->U32 << 8 + b := (a.z * 255)->U32 << 0 + result := r | g | b + return result diff --git a/build/modules/Multimedia.core b/modules/Multimedia.core similarity index 95% rename from build/modules/Multimedia.core rename to modules/Multimedia.core index a30d1fc..660862c 100644 --- a/build/modules/Multimedia.core +++ b/modules/Multimedia.core @@ -1,64 +1,64 @@ -/* - -Library for making games/graphical applications -* The api is very simple, very few function calls -* You retrieve information about the state of application from "Mu" struct -* Data is available in many different formats to avoid format conversions in user code - -API and name inspired by one of Per Vognsen streams -https://www.youtube.com/watch?v=NG_mUhc8LRw&list=PLU94OURih-CjrtFuazwZ5GYzTrupOMDL7&index=19 -All of his channel is recommended watch for programmers. - -*/ -Mu: MU - -MU :: struct - screen: *U32 - window: MUWindow - - key: [Key.Count]KeyState - mouse: Mouse - frame_count: U64 - time: MUTime - quit: bool - - frame_arena: Arena - os: Platform - -MUWindow :: struct - x: int - y: int - sizef: Vec2 - size: Vec2I - resizable: bool - -MUTime :: struct - total : F64 - delta : F64 // @modifiable - start : F64 - frame_start: F64 - -KeyState :: struct - down: bool - -Key :: enum - None - Up;Down;Left;Right;Escape;Control;Backspace;Alt;Shift;Tab - F1;F2;F3;F4;F5;F6;F7;F8;F9;F10 - F11;F12;A;B;C;D;E;F;G;H - I;J;K;L;M;N;O;P;Q;R - S;T;U;V;W;X;Y;Z;K0;K1 - K2;K3;K4;K5;K6;K7;K8;K9 - Count - -Mouse :: struct - left: KeyState - right: KeyState - middle: KeyState - wheel: int - -#import "Base.core" -#import "MathF32.core" -#import "MathVec2.core" -#import "Arena.core" -#load "$os_multimedia.core" +/* + +Library for making games/graphical applications +* The api is very simple, very few function calls +* You retrieve information about the state of application from "Mu" struct +* Data is available in many different formats to avoid format conversions in user code + +API and name inspired by one of Per Vognsen streams +https://www.youtube.com/watch?v=NG_mUhc8LRw&list=PLU94OURih-CjrtFuazwZ5GYzTrupOMDL7&index=19 +All of his channel is recommended watch for programmers. + +*/ +Mu: MU + +MU :: struct + screen: *U32 + window: MUWindow + + key: [Key.Count]KeyState + mouse: Mouse + frame_count: U64 + time: MUTime + quit: bool + + frame_arena: Arena + os: Platform + +MUWindow :: struct + x: int + y: int + sizef: Vec2 + size: Vec2I + resizable: bool + +MUTime :: struct + total : F64 + delta : F64 // @modifiable + start : F64 + frame_start: F64 + +KeyState :: struct + down: bool + +Key :: enum + None + Up;Down;Left;Right;Escape;Control;Backspace;Alt;Shift;Tab + F1;F2;F3;F4;F5;F6;F7;F8;F9;F10 + F11;F12;A;B;C;D;E;F;G;H + I;J;K;L;M;N;O;P;Q;R + S;T;U;V;W;X;Y;Z;K0;K1 + K2;K3;K4;K5;K6;K7;K8;K9 + Count + +Mouse :: struct + left: KeyState + right: KeyState + middle: KeyState + wheel: int + +#import "Base.core" +#import "MathF32.core" +#import "MathVec2.core" +#import "Arena.core" +#load "$os_multimedia.core" diff --git a/build/modules/OSWin32.core b/modules/OSWin32.core similarity index 96% rename from build/modules/OSWin32.core rename to modules/OSWin32.core index 299776e..6a4069f 100644 --- a/build/modules/OSWin32.core +++ b/modules/OSWin32.core @@ -1,65 +1,65 @@ -#import "KERNEL32.core" -#import "Base.core" - -PAGE_SIZE :: 4096 -Memory :: struct - commit : SizeU - reserve: SizeU - data : *U8 - -ProcessHeap: HANDLE -Allocate :: (size: U64): *void - if ProcessHeap == 0 - ProcessHeap = GetProcessHeap() - return HeapAlloc(ProcessHeap, 0, size) - -Reserve :: (size: SizeU): Memory - result := Memory{reserve=AlignUp(size, PAGE_SIZE)} - result.data = VirtualAlloc( - flProtect = PAGE_READWRITE, - dwSize = result.reserve, - flAllocationType = MEM_RESERVE, - lpAddress = 0)->*U8 - return result - -Commit :: (m: *Memory, size: SizeU): bool - commit_size := AlignUp(size, PAGE_SIZE) - total_commit := m.commit + commit_size - clamped_commit := ClampTopSizeU(total_commit, m.reserve) - adjusted_commit := clamped_commit - m.commit - if adjusted_commit != 0 - result := VirtualAlloc( - lpAddress = (m.data + m.commit)->*void, - dwSize = adjusted_commit, - flAllocationType = MEM_COMMIT, - flProtect = PAGE_READWRITE, - ) - Assert(result != 0) - m.commit += adjusted_commit - return true - return false - -Release :: (m: *Memory) - result := VirtualFree(m.data->*void, 0, MEM_RELEASE) - if result != 0 - m.data = 0 - m.commit = 0 - m.reserve = 0 - -WriteConsole :: (string: String16) - handle := GetStdHandle(STD_OUTPUT_HANDLE) - WriteConsoleW(handle, string.str->*void, string.len->DWORD, 0, 0) - -PerformanceFrequency: F64 -PerformanceFrequency_S64: S64 -Time :: (): F64 - query: LARGE_INTEGER - if PerformanceFrequency_S64 == 0 - err := QueryPerformanceFrequency(&PerformanceFrequency_S64) - Assert(err != 0) - PerformanceFrequency = PerformanceFrequency_S64->F64 - - err := QueryPerformanceCounter(&query) - Assert(err != 0) - result := query->F64 / PerformanceFrequency - return result +#import "KERNEL32.core" +#import "Base.core" + +PAGE_SIZE :: 4096 +Memory :: struct + commit : SizeU + reserve: SizeU + data : *U8 + +ProcessHeap: HANDLE +Allocate :: (size: U64): *void + if ProcessHeap == 0 + ProcessHeap = GetProcessHeap() + return HeapAlloc(ProcessHeap, 0, size) + +Reserve :: (size: SizeU): Memory + result := Memory{reserve=AlignUp(size, PAGE_SIZE)} + result.data = VirtualAlloc( + flProtect = PAGE_READWRITE, + dwSize = result.reserve, + flAllocationType = MEM_RESERVE, + lpAddress = 0)->*U8 + return result + +Commit :: (m: *Memory, size: SizeU): bool + commit_size := AlignUp(size, PAGE_SIZE) + total_commit := m.commit + commit_size + clamped_commit := ClampTopSizeU(total_commit, m.reserve) + adjusted_commit := clamped_commit - m.commit + if adjusted_commit != 0 + result := VirtualAlloc( + lpAddress = (m.data + m.commit)->*void, + dwSize = adjusted_commit, + flAllocationType = MEM_COMMIT, + flProtect = PAGE_READWRITE, + ) + Assert(result != 0) + m.commit += adjusted_commit + return true + return false + +Release :: (m: *Memory) + result := VirtualFree(m.data->*void, 0, MEM_RELEASE) + if result != 0 + m.data = 0 + m.commit = 0 + m.reserve = 0 + +WriteConsole :: (string: String16) + handle := GetStdHandle(STD_OUTPUT_HANDLE) + WriteConsoleW(handle, string.str->*void, string.len->DWORD, 0, 0) + +PerformanceFrequency: F64 +PerformanceFrequency_S64: S64 +Time :: (): F64 + query: LARGE_INTEGER + if PerformanceFrequency_S64 == 0 + err := QueryPerformanceFrequency(&PerformanceFrequency_S64) + Assert(err != 0) + PerformanceFrequency = PerformanceFrequency_S64->F64 + + err := QueryPerformanceCounter(&query) + Assert(err != 0) + result := query->F64 / PerformanceFrequency + return result diff --git a/build/modules/USER32.core b/modules/USER32.core similarity index 97% rename from build/modules/USER32.core rename to modules/USER32.core index 020f940..8a77e6e 100644 --- a/build/modules/USER32.core +++ b/modules/USER32.core @@ -1,200 +1,200 @@ -#import "KERNEL32.core" -#link "user32" -WNDPROC :: (hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM): LRESULT -WNDCLASSW :: struct;; style: UINT; lpfnWndProc: WNDPROC; cbClsExtra: int; cbWndExtra: int; hInstance: HINSTANCE; hIcon: HICON; hCursor: HCURSOR; hbrBackground: HBRUSH; lpszMenuName: LPCWSTR; lpszClassName: LPCWSTR -MSG :: struct;; hwnd: HWND; message: UINT; wParam: WPARAM; lParam: LPARAM; time: DWORD; pt: POINT; lPrivate: DWORD -POINT :: struct;; x: LONG; y: LONG -LPMSG :: *MSG -RECT :: struct;; left: LONG; top: LONG; right: LONG; bottom: LONG -LPRECT :: *RECT - -PostQuitMessage :: #foreign (nExitCode: int) -DefWindowProcW :: #foreign (hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM): LRESULT -GetDC :: #foreign (hWnd: HWND): HDC -CreateWindowA :: #foreign (dwExStyle: DWORD, lpClassName: *char, lpWindowName: *char, dwStyle: DWORD, X: int, Y: int, nWidth: int, nHeight: int, hWndParent: HWND, hMenu: HMENU, hInstance: HINSTANCE, lpParam: *void): HWND -CreateWindowExW :: #foreign (dwExStyle: DWORD, lpClassName: LPCWSTR, lpWindowName: LPCWSTR, dwStyle: DWORD, X: int, Y: int, nWidth: int, nHeight: int, hWndParent: HWND, hMenu: HMENU, hInstance: HINSTANCE, lpParam: LPVOID): HWND -RegisterClassW :: #foreign (lpWndClass: *WNDCLASSW): ATOM -ShowWindow :: #foreign (hWnd: HWND, nCmdShow: int): BOOL -PeekMessageW :: #foreign (lpMsg: LPMSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMs: UINT):BOOL -TranslateMessage :: #foreign (lpMsg: *MSG): BOOL -DispatchMessageW :: #foreign (lpMsg: *MSG): LRESULT -SetProcessDPIAware:: #foreign (): BOOL -GetDpiForWindow :: #foreign (hwnd: HWND): UINT -AdjustWindowRectExForDpi :: #foreign (lpRect: *RECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT): BOOL -AdjustWindowRectEx :: #foreign (lpRect: *RECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD): BOOL -SetWindowPos :: #foreign (hWnd: HWND, hWndInsertAfter: HWND, X: int, Y: int, cx: int, cy: int, uFlags: UINT): BOOL -GetClientRect :: #foreign (hWnd: HWND, lpRect: LPRECT): BOOL - -CW_USEDEFAULT :: 0x80000000-1 // -2147483648 - - -WS_CAPTION :: 0x00C00000 -WS_CHILD :: 0x40000000 -WS_CHILDWINDOW :: 0x40000000 -WS_CLIPCHILDREN :: 0x02000000 -WS_CLIPSIBLINGS :: 0x04000000 -WS_DISABLED :: 0x08000000 -WS_DLGFRAME :: 0x00400000 -WS_GROUP :: 0x00020000 -WS_HSCROLL :: 0x00100000 -WS_ICONIC :: 0x20000000 -WS_MAXIMIZE :: 0x01000000 -WS_MAXIMIZEBOX :: 0x00010000 -WS_MINIMIZE :: 0x20000000 -WS_MINIMIZEBOX :: 0x00020000 -WS_OVERLAPPED :: 0x00000000 -WS_POPUP :: 0x80000000 -WS_SIZEBOX :: 0x00040000 -WS_SYSMENU :: 0x00080000 -WS_TABSTOP :: 0x00010000 -WS_THICKFRAME :: 0x00040000 -WS_TILED :: 0x00000000 -WS_VISIBLE :: 0x10000000 -WS_VSCROLL :: 0x00200000 -WS_OVERLAPPEDWINDOW :: WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX - -PM_NOREMOVE :: 0 -PM_REMOVE :: 0x0001 -PM_NOYIELD :: 0x0002 - -SW_HIDE :: 0 -SW_NORMAL :: 1 -SW_SHOWMINIMIZED :: 2 -SW_SHOWMAXIMIZED :: 3 -SW_SHOWNOACTIVATE :: 4 -SW_SHOW :: 5 -SW_MINIMIZE :: 6 -SW_SHOWMINNOACTIVE :: 7 -SW_SHOWNA :: 8 -SW_RESTORE :: 9 -SW_SHOWDEFAULT :: 10 -SW_FORCEMINIMIZE :: 11 - -HWND_TOP :: 0 -HWND_BOTTOM :: 1 -// HWND_NOTOPMOST :: -2 // Probably relies on overflow ? -// HWND_TOPMOST :: -1 // Probably relies on overflow ? - -SWP_ASYNCWINDOWPOS :: 0x4000 -SWP_DEFERERASE :: 0x2000 -SWP_DRAWFRAME :: 0x0020 -SWP_FRAMECHANGED :: 0x0020 -SWP_HIDEWINDOW :: 0x0080 -SWP_NOACTIVATE :: 0x0010 -SWP_NOCOPYBITS :: 0x0100 -SWP_NOMOVE :: 0x0002 -SWP_NOOWNERZORDER :: 0x0200 -SWP_NOREDRAW :: 0x0008 -SWP_NOREPOSITION :: 0x0200 -SWP_NOSENDCHANGING :: 0x0400 -SWP_NOSIZE :: 0x0001 -SWP_NOZORDER :: 0x0004 -SWP_SHOWWINDOW :: 0x0040 - -WM_NULL :: 0x0000; WM_CREATE :: 0x0001; WM_DESTROY :: 0x0002; WM_MOVE :: 0x0003; WM_SIZE :: 0x0005 -WM_ACTIVATE :: 0x0006; WA_INACTIVE :: 0; WA_ACTIVE :: 1; WA_CLICKACTIVE :: 2 -WM_SETFOCUS :: 0x0007; WM_KILLFOCUS :: 0x0008; WM_ENABLE :: 0x000A; WM_SETREDRAW :: 0x000B; WM_SETTEXT :: 0x000C; WM_GETTEXT :: 0x000D; WM_GETTEXTLENGTH :: 0x000E; WM_PAINT :: 0x000F; WM_CLOSE :: 0x0010 -WM_KEYFIRST :: 0x0100 -WM_KEYDOWN :: 0x0100 -WM_KEYUP :: 0x0101 -WM_CHAR :: 0x0102 -WM_DEADCHAR :: 0x0103 -WM_SYSKEYDOWN :: 0x0104 -WM_SYSKEYUP :: 0x0105 -WM_SYSCHAR :: 0x0106 -WM_SYSDEADCHAR :: 0x0107 - -WM_MOUSEFIRST :: 0x0200 -WM_MOUSEMOVE :: 0x0200 -WM_LBUTTONDOWN :: 0x0201 -WM_LBUTTONUP :: 0x0202 -WM_LBUTTONDBLCLK :: 0x0203 -WM_RBUTTONDOWN :: 0x0204 -WM_RBUTTONUP :: 0x0205 -WM_RBUTTONDBLCLK :: 0x0206 -WM_MBUTTONDOWN :: 0x0207 -WM_MBUTTONUP :: 0x0208 -WM_MBUTTONDBLCLK :: 0x0209 -WM_MOUSEWHEEL :: 0x020A - - -VK_BACK :: 0x08 -VK_TAB :: 0x09 -VK_CLEAR :: 0x0C -VK_RETURN :: 0x0D -VK_SHIFT :: 0x10 -VK_CONTROL :: 0x11 -VK_MENU :: 0x12 -VK_PAUSE :: 0x13 -VK_CAPITAL :: 0x14 - -VK_KANA :: 0x15 -VK_HANGEUL :: 0x15 /* old name - should be here for compatibility */ -VK_HANGUL :: 0x15 -VK_IME_ON :: 0x16 -VK_JUNJA :: 0x17 -VK_FINAL :: 0x18 -VK_HANJA :: 0x19 -VK_KANJI :: 0x19 -VK_IME_OFF :: 0x1A - -VK_ESCAPE :: 0x1B -VK_CONVERT :: 0x1C -VK_NONCONVERT :: 0x1D -VK_ACCEPT :: 0x1E -VK_MODECHANGE :: 0x1F -VK_SPACE :: 0x20 -VK_PRIOR :: 0x21 -VK_NEXT :: 0x22 -VK_END :: 0x23 -VK_HOME :: 0x24 -VK_LEFT :: 0x25 -VK_UP :: 0x26 -VK_RIGHT :: 0x27 -VK_DOWN :: 0x28 -VK_SELECT :: 0x29 -VK_PRINT :: 0x2A -VK_EXECUTE :: 0x2B -VK_SNAPSHOT :: 0x2C -VK_INSERT :: 0x2D -VK_DELETE :: 0x2E -VK_HELP :: 0x2F - -/* - * VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - 0x39) - * 0x3A - 0x40 : unassigned - * VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A) - */ - -VK_LWIN :: 0x5B -VK_RWIN :: 0x5C -VK_APPS :: 0x5D -VK_SLEEP :: 0x5F -VK_NUMPAD0 :: 0x60 -VK_NUMPAD1 :: 0x61 -VK_NUMPAD2 :: 0x62 -VK_NUMPAD3 :: 0x63 -VK_NUMPAD4 :: 0x64 -VK_NUMPAD5 :: 0x65 -VK_NUMPAD6 :: 0x66 -VK_NUMPAD7 :: 0x67 -VK_NUMPAD8 :: 0x68 -VK_NUMPAD9 :: 0x69 -VK_MULTIPLY :: 0x6A -VK_ADD :: 0x6B -VK_SEPARATOR :: 0x6C -VK_SUBTRACT :: 0x6D -VK_DECIMAL :: 0x6E -VK_DIVIDE :: 0x6F -VK_F1 :: 0x70 -VK_F2 :: 0x71 -VK_F3 :: 0x72 -VK_F4 :: 0x73 -VK_F5 :: 0x74 -VK_F6 :: 0x75 -VK_F7 :: 0x76 -VK_F8 :: 0x77 -VK_F9 :: 0x78 -VK_F10 :: 0x79 -VK_F11 :: 0x7A +#import "KERNEL32.core" +#link "user32" +WNDPROC :: (hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM): LRESULT +WNDCLASSW :: struct;; style: UINT; lpfnWndProc: WNDPROC; cbClsExtra: int; cbWndExtra: int; hInstance: HINSTANCE; hIcon: HICON; hCursor: HCURSOR; hbrBackground: HBRUSH; lpszMenuName: LPCWSTR; lpszClassName: LPCWSTR +MSG :: struct;; hwnd: HWND; message: UINT; wParam: WPARAM; lParam: LPARAM; time: DWORD; pt: POINT; lPrivate: DWORD +POINT :: struct;; x: LONG; y: LONG +LPMSG :: *MSG +RECT :: struct;; left: LONG; top: LONG; right: LONG; bottom: LONG +LPRECT :: *RECT + +PostQuitMessage :: #foreign (nExitCode: int) +DefWindowProcW :: #foreign (hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM): LRESULT +GetDC :: #foreign (hWnd: HWND): HDC +CreateWindowA :: #foreign (dwExStyle: DWORD, lpClassName: *char, lpWindowName: *char, dwStyle: DWORD, X: int, Y: int, nWidth: int, nHeight: int, hWndParent: HWND, hMenu: HMENU, hInstance: HINSTANCE, lpParam: *void): HWND +CreateWindowExW :: #foreign (dwExStyle: DWORD, lpClassName: LPCWSTR, lpWindowName: LPCWSTR, dwStyle: DWORD, X: int, Y: int, nWidth: int, nHeight: int, hWndParent: HWND, hMenu: HMENU, hInstance: HINSTANCE, lpParam: LPVOID): HWND +RegisterClassW :: #foreign (lpWndClass: *WNDCLASSW): ATOM +ShowWindow :: #foreign (hWnd: HWND, nCmdShow: int): BOOL +PeekMessageW :: #foreign (lpMsg: LPMSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMs: UINT):BOOL +TranslateMessage :: #foreign (lpMsg: *MSG): BOOL +DispatchMessageW :: #foreign (lpMsg: *MSG): LRESULT +SetProcessDPIAware:: #foreign (): BOOL +GetDpiForWindow :: #foreign (hwnd: HWND): UINT +AdjustWindowRectExForDpi :: #foreign (lpRect: *RECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT): BOOL +AdjustWindowRectEx :: #foreign (lpRect: *RECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD): BOOL +SetWindowPos :: #foreign (hWnd: HWND, hWndInsertAfter: HWND, X: int, Y: int, cx: int, cy: int, uFlags: UINT): BOOL +GetClientRect :: #foreign (hWnd: HWND, lpRect: LPRECT): BOOL + +CW_USEDEFAULT :: 0x80000000-1 // -2147483648 + + +WS_CAPTION :: 0x00C00000 +WS_CHILD :: 0x40000000 +WS_CHILDWINDOW :: 0x40000000 +WS_CLIPCHILDREN :: 0x02000000 +WS_CLIPSIBLINGS :: 0x04000000 +WS_DISABLED :: 0x08000000 +WS_DLGFRAME :: 0x00400000 +WS_GROUP :: 0x00020000 +WS_HSCROLL :: 0x00100000 +WS_ICONIC :: 0x20000000 +WS_MAXIMIZE :: 0x01000000 +WS_MAXIMIZEBOX :: 0x00010000 +WS_MINIMIZE :: 0x20000000 +WS_MINIMIZEBOX :: 0x00020000 +WS_OVERLAPPED :: 0x00000000 +WS_POPUP :: 0x80000000 +WS_SIZEBOX :: 0x00040000 +WS_SYSMENU :: 0x00080000 +WS_TABSTOP :: 0x00010000 +WS_THICKFRAME :: 0x00040000 +WS_TILED :: 0x00000000 +WS_VISIBLE :: 0x10000000 +WS_VSCROLL :: 0x00200000 +WS_OVERLAPPEDWINDOW :: WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX + +PM_NOREMOVE :: 0 +PM_REMOVE :: 0x0001 +PM_NOYIELD :: 0x0002 + +SW_HIDE :: 0 +SW_NORMAL :: 1 +SW_SHOWMINIMIZED :: 2 +SW_SHOWMAXIMIZED :: 3 +SW_SHOWNOACTIVATE :: 4 +SW_SHOW :: 5 +SW_MINIMIZE :: 6 +SW_SHOWMINNOACTIVE :: 7 +SW_SHOWNA :: 8 +SW_RESTORE :: 9 +SW_SHOWDEFAULT :: 10 +SW_FORCEMINIMIZE :: 11 + +HWND_TOP :: 0 +HWND_BOTTOM :: 1 +// HWND_NOTOPMOST :: -2 // Probably relies on overflow ? +// HWND_TOPMOST :: -1 // Probably relies on overflow ? + +SWP_ASYNCWINDOWPOS :: 0x4000 +SWP_DEFERERASE :: 0x2000 +SWP_DRAWFRAME :: 0x0020 +SWP_FRAMECHANGED :: 0x0020 +SWP_HIDEWINDOW :: 0x0080 +SWP_NOACTIVATE :: 0x0010 +SWP_NOCOPYBITS :: 0x0100 +SWP_NOMOVE :: 0x0002 +SWP_NOOWNERZORDER :: 0x0200 +SWP_NOREDRAW :: 0x0008 +SWP_NOREPOSITION :: 0x0200 +SWP_NOSENDCHANGING :: 0x0400 +SWP_NOSIZE :: 0x0001 +SWP_NOZORDER :: 0x0004 +SWP_SHOWWINDOW :: 0x0040 + +WM_NULL :: 0x0000; WM_CREATE :: 0x0001; WM_DESTROY :: 0x0002; WM_MOVE :: 0x0003; WM_SIZE :: 0x0005 +WM_ACTIVATE :: 0x0006; WA_INACTIVE :: 0; WA_ACTIVE :: 1; WA_CLICKACTIVE :: 2 +WM_SETFOCUS :: 0x0007; WM_KILLFOCUS :: 0x0008; WM_ENABLE :: 0x000A; WM_SETREDRAW :: 0x000B; WM_SETTEXT :: 0x000C; WM_GETTEXT :: 0x000D; WM_GETTEXTLENGTH :: 0x000E; WM_PAINT :: 0x000F; WM_CLOSE :: 0x0010 +WM_KEYFIRST :: 0x0100 +WM_KEYDOWN :: 0x0100 +WM_KEYUP :: 0x0101 +WM_CHAR :: 0x0102 +WM_DEADCHAR :: 0x0103 +WM_SYSKEYDOWN :: 0x0104 +WM_SYSKEYUP :: 0x0105 +WM_SYSCHAR :: 0x0106 +WM_SYSDEADCHAR :: 0x0107 + +WM_MOUSEFIRST :: 0x0200 +WM_MOUSEMOVE :: 0x0200 +WM_LBUTTONDOWN :: 0x0201 +WM_LBUTTONUP :: 0x0202 +WM_LBUTTONDBLCLK :: 0x0203 +WM_RBUTTONDOWN :: 0x0204 +WM_RBUTTONUP :: 0x0205 +WM_RBUTTONDBLCLK :: 0x0206 +WM_MBUTTONDOWN :: 0x0207 +WM_MBUTTONUP :: 0x0208 +WM_MBUTTONDBLCLK :: 0x0209 +WM_MOUSEWHEEL :: 0x020A + + +VK_BACK :: 0x08 +VK_TAB :: 0x09 +VK_CLEAR :: 0x0C +VK_RETURN :: 0x0D +VK_SHIFT :: 0x10 +VK_CONTROL :: 0x11 +VK_MENU :: 0x12 +VK_PAUSE :: 0x13 +VK_CAPITAL :: 0x14 + +VK_KANA :: 0x15 +VK_HANGEUL :: 0x15 /* old name - should be here for compatibility */ +VK_HANGUL :: 0x15 +VK_IME_ON :: 0x16 +VK_JUNJA :: 0x17 +VK_FINAL :: 0x18 +VK_HANJA :: 0x19 +VK_KANJI :: 0x19 +VK_IME_OFF :: 0x1A + +VK_ESCAPE :: 0x1B +VK_CONVERT :: 0x1C +VK_NONCONVERT :: 0x1D +VK_ACCEPT :: 0x1E +VK_MODECHANGE :: 0x1F +VK_SPACE :: 0x20 +VK_PRIOR :: 0x21 +VK_NEXT :: 0x22 +VK_END :: 0x23 +VK_HOME :: 0x24 +VK_LEFT :: 0x25 +VK_UP :: 0x26 +VK_RIGHT :: 0x27 +VK_DOWN :: 0x28 +VK_SELECT :: 0x29 +VK_PRINT :: 0x2A +VK_EXECUTE :: 0x2B +VK_SNAPSHOT :: 0x2C +VK_INSERT :: 0x2D +VK_DELETE :: 0x2E +VK_HELP :: 0x2F + +/* + * VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - 0x39) + * 0x3A - 0x40 : unassigned + * VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A) + */ + +VK_LWIN :: 0x5B +VK_RWIN :: 0x5C +VK_APPS :: 0x5D +VK_SLEEP :: 0x5F +VK_NUMPAD0 :: 0x60 +VK_NUMPAD1 :: 0x61 +VK_NUMPAD2 :: 0x62 +VK_NUMPAD3 :: 0x63 +VK_NUMPAD4 :: 0x64 +VK_NUMPAD5 :: 0x65 +VK_NUMPAD6 :: 0x66 +VK_NUMPAD7 :: 0x67 +VK_NUMPAD8 :: 0x68 +VK_NUMPAD9 :: 0x69 +VK_MULTIPLY :: 0x6A +VK_ADD :: 0x6B +VK_SEPARATOR :: 0x6C +VK_SUBTRACT :: 0x6D +VK_DECIMAL :: 0x6E +VK_DIVIDE :: 0x6F +VK_F1 :: 0x70 +VK_F2 :: 0x71 +VK_F3 :: 0x72 +VK_F4 :: 0x73 +VK_F5 :: 0x74 +VK_F6 :: 0x75 +VK_F7 :: 0x76 +VK_F8 :: 0x77 +VK_F9 :: 0x78 +VK_F10 :: 0x79 +VK_F11 :: 0x7A VK_F12 :: 0x7B \ No newline at end of file diff --git a/build/modules/WINMM.core b/modules/WINMM.core similarity index 96% rename from build/modules/WINMM.core rename to modules/WINMM.core index a73fb14..af6b5ee 100644 --- a/build/modules/WINMM.core +++ b/modules/WINMM.core @@ -1,6 +1,6 @@ -#import "KERNEL32.core" -#link "winmm" - -MMRESULT :: UINT -TIMERR_NOERROR :: 0 +#import "KERNEL32.core" +#link "winmm" + +MMRESULT :: UINT +TIMERR_NOERROR :: 0 timeBeginPeriod :: #foreign (uPeriod: UINT): MMRESULT \ No newline at end of file diff --git a/build/modules/array.core b/modules/array.core similarity index 95% rename from build/modules/array.core rename to modules/array.core index b369d30..d00e9e8 100644 --- a/build/modules/array.core +++ b/modules/array.core @@ -1,101 +1,101 @@ -#import "LibC.core" - -Array :: struct($T: Type) - data: *T - len: int - cap: int - -Pop :: (a: *Array($T)): T - if a.len > 0 - a.len -= 1 - return a.data[a.len] - return {} - -Contains :: (a: *Array($T), item: *T): bool - result := item >= a.data && item < a.data + a.len - return result - -Free :: (a: *Array($T)) - free(a.data) - a.cap = 0; a.len = 0; a.data = 0 - -Reset :: (a: *Array($T)) - a.len = 0 - -GetLast :: (a: *Array($T)): *T - Assert(a.len > 0) - result := a.data + a.len-1 - return result - -Insert :: (a: *Array($T), item: T, index: int) - if index == a.len - Add(a, item) - return - - Assert(index < a.len) - Assert(index >= 0) - - TryGrowing(a) - right_len := (a.len - index)->size_t - memmove(a.data + index + 1, a.data + index, sizeof(T) * right_len) - a.data[index] = item - a.len += 1 - -GetIndex :: (a: *Array($T), item: *T): int - Assert(a.len > 0) - Assert(item >= a.data && item < a.data + a.len) - - index := (item - a.data)->int - Assert(index >= 0 && index < a.len) - return index - -UnorderedRemove :: (a: *Array($T), item: *T) - Assert(a.len > 0) - Assert(item >= a.data && item < a.data + a.len) - - *item = a.data[--a.len] - -OrderedRemove :: (a: *Array($T), item: *T) - index := GetIndex(a, item) - if index == a.len - 1 - Pop(a) - return - - length_right_of_item := (a.len - index - 1)->size_t - memmove(a.data + index, a.data + index + 1, length_right_of_item * sizeof(T)) - a.len -= 1 - -TryGrowing :: (a: *Array($T)) - if a.cap == 0 - a.cap = 16 - a.data = malloc(sizeof(T) * a.cap->size_t) - if a.len + 1 > a.cap - a.cap *= 2 - a.data = realloc(a.data, sizeof(T) * a.cap->size_t) - -Add :: (a: *Array($T), item: T) - TryGrowing(a) - a.data[a.len++] = item - -BoundedAdd :: (a: *Array($T), item: T) - Assert(a.len + 1 <= a.cap) - Add(a, item) - -InsertSortedDecreasing :: (a: *Array($T), item: T) - insert_index := -1 - for i := 0, i < a.len, i += 1 - it := a.data + i - if it.value_to_sort_by <= item.value_to_sort_by - insert_index = i - Insert(a, item, i) - break - - if insert_index == -1 - Add(a, item) - -Reserve :: (a: *Array($T), size: int) - Assert(size > a.cap) - a.cap = size - p := realloc(a.data, sizeof(T) * a.cap->size_t) - Assert(p != 0) +#import "LibC.core" + +Array :: struct($T: Type) + data: *T + len: int + cap: int + +Pop :: (a: *Array($T)): T + if a.len > 0 + a.len -= 1 + return a.data[a.len] + return {} + +Contains :: (a: *Array($T), item: *T): bool + result := item >= a.data && item < a.data + a.len + return result + +Free :: (a: *Array($T)) + free(a.data) + a.cap = 0; a.len = 0; a.data = 0 + +Reset :: (a: *Array($T)) + a.len = 0 + +GetLast :: (a: *Array($T)): *T + Assert(a.len > 0) + result := a.data + a.len-1 + return result + +Insert :: (a: *Array($T), item: T, index: int) + if index == a.len + Add(a, item) + return + + Assert(index < a.len) + Assert(index >= 0) + + TryGrowing(a) + right_len := (a.len - index)->size_t + memmove(a.data + index + 1, a.data + index, sizeof(T) * right_len) + a.data[index] = item + a.len += 1 + +GetIndex :: (a: *Array($T), item: *T): int + Assert(a.len > 0) + Assert(item >= a.data && item < a.data + a.len) + + index := (item - a.data)->int + Assert(index >= 0 && index < a.len) + return index + +UnorderedRemove :: (a: *Array($T), item: *T) + Assert(a.len > 0) + Assert(item >= a.data && item < a.data + a.len) + + *item = a.data[--a.len] + +OrderedRemove :: (a: *Array($T), item: *T) + index := GetIndex(a, item) + if index == a.len - 1 + Pop(a) + return + + length_right_of_item := (a.len - index - 1)->size_t + memmove(a.data + index, a.data + index + 1, length_right_of_item * sizeof(T)) + a.len -= 1 + +TryGrowing :: (a: *Array($T)) + if a.cap == 0 + a.cap = 16 + a.data = malloc(sizeof(T) * a.cap->size_t) + if a.len + 1 > a.cap + a.cap *= 2 + a.data = realloc(a.data, sizeof(T) * a.cap->size_t) + +Add :: (a: *Array($T), item: T) + TryGrowing(a) + a.data[a.len++] = item + +BoundedAdd :: (a: *Array($T), item: T) + Assert(a.len + 1 <= a.cap) + Add(a, item) + +InsertSortedDecreasing :: (a: *Array($T), item: T) + insert_index := -1 + for i := 0, i < a.len, i += 1 + it := a.data + i + if it.value_to_sort_by <= item.value_to_sort_by + insert_index = i + Insert(a, item, i) + break + + if insert_index == -1 + Add(a, item) + +Reserve :: (a: *Array($T), size: int) + Assert(size > a.cap) + a.cap = size + p := realloc(a.data, sizeof(T) * a.cap->size_t) + Assert(p != 0) a.data = p \ No newline at end of file diff --git a/build/modules/raylib.core b/modules/raylib.core similarity index 98% rename from build/modules/raylib.core rename to modules/raylib.core index 81d2e47..aa3f9a3 100644 --- a/build/modules/raylib.core +++ b/modules/raylib.core @@ -1,1138 +1,1138 @@ - -PI :: 3.14159265358979323846 -DEG2RAD :: PI/180.0 -RAD2DEG :: 180.0/PI - -Vector2 :: struct - x: F32 - y: F32 - -Vector3 :: struct - x: F32 - y: F32 - z: F32 - -Vector4 :: struct - x: F32 - y: F32 - z: F32 - w: F32 - -Color :: struct - r: uchar - g: uchar - b: uchar - a: uchar - -Rectangle :: struct - x: F32 - y: F32 - width: F32 - height: F32 - -Image :: struct - data: *void - width: int - height: int - mipmaps: int - format: int - -Texture :: struct - id: uint - width: int - height: int - mipmaps: int - format: int - -Texture2D :: Texture -TextureCubemap :: Texture - -RenderTexture :: struct - id: uint - texture: Texture - depth: Texture - -RenderTexture2D :: RenderTexture - -// N-Patch layout info -NPatchInfo :: struct - source: Rectangle // Texture source rectangle - left: int // Left border offset - top: int // Top border offset - right: int // Right border offset - bottom: int // Bottom border offset - layout: NPatchLayout // Layout of the n-patch: 3x3 1x3 or 3x1 - -// Font character info -GlyphInfo :: struct - value: rune // Character value (Unicode) - offsetX: int // Character offset X when drawing - offsetY: int // Character offset Y when drawing - advanceX: int // Character advance position X - image: Image // Character image data - -// Font type includes texture and charSet array data -Font :: struct - baseSize: int // Base size (default chars height) - charsCount: int // Number of characters - charsPadding: int // Padding around the chars - texture: Texture2D // Characters texture atlas - recs: *Rectangle // Characters rectangles in texture - chars: *GlyphInfo // Characters info data - -// Camera type defines a camera position/orientation in 3d space -Camera3D :: struct - position: Vector3 // Camera position - 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 - -Camera :: Camera3D // Camera type fallback defaults to Camera3D - -// Camera2D type defines a 2d camera -Camera2D :: struct - offset: Vector2 // Camera offset (displacement from target) - target: Vector2 // Camera target (rotation and zoom origin) - rotation: F32 // Camera rotation in degrees - zoom: F32 // Camera zoom (scaling) should be 1.0f by default - -// Vertex data definning a mesh -// NOTE: Data stored in CPU memory (and GPU) -Mesh :: struct - vertexCount: int // Number of vertices stored in arrays - triangleCount: int // Number of triangles stored (indexed or not) - - // Default vertex data - 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) - texcoords2: *F32 // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) - normals: *F32 // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) - tangents: *F32 // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) - colors: *U8 // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) - indices: *U16 // Vertex indices (in case vertex data comes indexed) - - // Animation vertex data - animVertices: *F32 // Animated vertex positions (after bones transformations) - animNormals: *F32 // Animated normals (after bones transformations) - boneIds: *U8 // Vertex bone ids up to 4 bones influence by vertex (skinning) - boneWeights: *F32 // Vertex bone weight up to 4 bones influence by vertex (skinning) - - // OpenGL identifiers - vaoId: U32 // OpenGL Vertex Array Object id - vboId: *U32 // OpenGL Vertex Buffer Objects id (default vertex data) - -// Shader type (generic) -Shader :: struct - id: uint // Shader program id - locs: *int // Shader locations array (MAX_SHADER_LOCATIONS) - -// Material texture map -MaterialMap :: struct - texture: Texture2D // Material map texture - color: Color // Material map color - value: F32 // Material map value - -// Material type (generic) -Material :: struct - shader: Shader // Material shader - maps: *MaterialMap // Material maps array (MAX_MATERIAL_MAPS) - params: [4]F32 // Material generic parameters (if required) - -// Transformation properties -Transform :: struct - translation: Vector3 // Translation - rotation: Quaternion // Rotation - scale: Vector3 // Scale - -// Bone information -BoneInfo :: struct - name: [32]char // Bone name - parent: int // Bone parent - -// Model type -Model :: struct - transform: Matrix // Local transform matrix - - meshCount: int // Number of meshes - materialCount: int // Number of materials - meshes: *Mesh // Meshes array - materials: *Material // Materials array - meshMaterial: *int // Mesh material number - - // Animation data - boneCount: int // Number of bones - bones: *BoneInfo // Bones information (skeleton) - bindPose: *Transform // Bones base transformation (pose) - -// Model animation -ModelAnimation :: struct - boneCount: int // Number of bones - frameCount: int // Number of animation frames - bones: *BoneInfo // Bones information (skeleton) - framePoses: **Transform // Poses array by frame - -// Ray type (useful for raycast) -Ray :: struct - position: Vector3 // Ray position (origin) - direction: Vector3 // Ray direction - -// RayCollision ray hit information -RayCollision :: struct - hit: bool // Did the ray hit something? - distance: F32 // Distance to nearest hit - point: Vector3 // Point of nearest hit - normal: Vector3 // Surface normal of hit - -// Bounding box type -BoundingBox :: struct - min: Vector3 // Minimum vertex box-corner - max: Vector3 // Maximum vertex box-corner - -// Wave type defines audio wave data -Wave :: struct - frameCount: uint // Total number of frames (considering channels) - sampleRate: uint // Frequency (samples per second) - sampleSize: uint // Bit depth (bits per sample): 8 16 32 (24 not supported) - channels: uint // Number of channels (1-mono 2-stereo) - data: *void // Buffer data pointer - -// Audio stream type -// NOTE: Actual structs are defined internally in raudio module -AudioStream :: struct - buffer: *void // Pointer to internal data used by the audio system - processor: *void // Pointer to internal data processor useful for audio effects - - sampleRate: uint // Frequency (samples per second) - sampleSize: uint // Bit depth (bits per sample): 8 16 32 (24 not supported) - channels: uint // Number of channels (1-mono 2-stereo) - -// Sound source type -Sound :: struct - stream: AudioStream // Audio stream - frameCount: uint // Total number of frames (considering channels) - -// Music stream type (audio file streaming from memory) -// NOTE: Anything longer than ~10 seconds should be streamed -Music :: struct - stream: AudioStream // Audio stream - frameCount: uint // Total number of frames (considering channels) - looping: bool // Music looping enable - - ctxType: int // Type of music context (audio filetype) - ctxData: *void // Audio context data depends on type - -// Head-Mounted-Display device parameters -VrDeviceInfo :: struct - hResolution: int // Horizontal resolution in pixels - vResolution: int // Vertical resolution in pixels - hScreenSize: F32 // Horizontal size in meters - vScreenSize: F32 // Vertical size in meters - vScreenCenter: F32 // Screen center in meters - eyeToScreenDistance: F32 // Distance between eye and display in meters - lensSeparationDistance: F32 // Lens separation distance in meters - interpupillaryDistance: F32 // IPD (distance between pupils) in meters - lensDistortionValues: [4]F32 // Lens distortion constant parameters - chromaAbCorrection: [4]F32 // Chromatic aberration correction parameters - -// VR Stereo rendering configuration for simulator -VrStereoConfig :: struct - projection: [2]Matrix // VR projection matrices (per eye) - viewOffset: [2]Matrix // VR view offset matrices (per eye) - leftLensCenter: [2]F32 // VR left lens center - rightLensCenter: [2]F32 // VR right lens center - leftScreenCenter: [2]F32 // VR left screen center - rightScreenCenter: [2]F32 // VR right screen center - scale: [2]F32 // VR distortion scale - scaleIn: [2]F32 // VR distortion scale in - -// File path list -FilePathList :: struct - capacity: uint // Filepaths max entries - count: uint // Filepaths entries count - paths: **char // Filepaths entries - -// 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 -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) - -FLAG_VSYNC_HINT :: 0x00000040 // Set to try enabling V-Sync on GPU -FLAG_FULLSCREEN_MODE :: 0x00000002 // Set to run program in fullscreen -FLAG_WINDOW_RESIZABLE :: 0x00000004 // Set to allow resizable window -FLAG_WINDOW_UNDECORATED :: 0x00000008 // Set to disable window decoration (frame and buttons) -FLAG_WINDOW_HIDDEN :: 0x00000080 // Set to hide window -FLAG_WINDOW_MINIMIZED :: 0x00000200 // Set to minimize window (iconify) -FLAG_WINDOW_MAXIMIZED :: 0x00000400 // Set to maximize window (expanded to monitor) -FLAG_WINDOW_UNFOCUSED :: 0x00000800 // Set to window non focused -FLAG_WINDOW_TOPMOST :: 0x00001000 // Set to window always on top -FLAG_WINDOW_ALWAYS_RUN :: 0x00000100 // Set to allow windows running while minimized -FLAG_WINDOW_TRANSPARENT :: 0x00000010 // Set to allow transparent framebuffer -FLAG_WINDOW_HIGHDPI :: 0x00002000 // Set to support HighDPI -FLAG_WINDOW_MOUSE_PASSTHROUGH :: 0x00004000 // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED -FLAG_MSAA_4X_HINT :: 0x00000020 // Set to try enabling MSAA 4X -FLAG_INTERLACED_HINT :: 0x00010000 // Set to try enabling interlaced video format (for V3D) - -LOG_ALL :: 0 // Display all logs -LOG_TRACE :: 1 // Trace logging, intended for internal use only -LOG_DEBUG :: 2 // Debug logging, used for internal debugging, it should be disabled on release builds -LOG_INFO :: 3 // Info logging, used for program execution info -LOG_WARNING :: 4 // Warning logging, used on recoverable failures -LOG_ERROR :: 5 // Error logging, used on unrecoverable failures -LOG_FATAL :: 6 // Fatal logging, used to abort program: exit(EXIT_FAILURE) -LOG_NONE :: 7 // Disable logging - -KEY_NULL :: 0 // Key: NULL, used for no key pressed -// Alphanumeric keys -KEY_APOSTROPHE :: 39 // Key: ' -KEY_COMMA :: 44 // Key: , -KEY_MINUS :: 45 // Key: - -KEY_PERIOD :: 46 // Key: . -KEY_SLASH :: 47 // Key: / -KEY_ZERO :: 48 // Key: 0 -KEY_ONE :: 49 // Key: 1 -KEY_TWO :: 50 // Key: 2 -KEY_THREE :: 51 // Key: 3 -KEY_FOUR :: 52 // Key: 4 -KEY_FIVE :: 53 // Key: 5 -KEY_SIX :: 54 // Key: 6 -KEY_SEVEN :: 55 // Key: 7 -KEY_EIGHT :: 56 // Key: 8 -KEY_NINE :: 57 // Key: 9 -KEY_SEMICOLON :: 59 // Key: ; -KEY_EQUAL :: 61 // Key: :: -KEY_A :: 65 // Key: A | a -KEY_B :: 66 // Key: B | b -KEY_C :: 67 // Key: C | c -KEY_D :: 68 // Key: D | d -KEY_E :: 69 // Key: E | e -KEY_F :: 70 // Key: F | f -KEY_G :: 71 // Key: G | g -KEY_H :: 72 // Key: H | h -KEY_I :: 73 // Key: I | i -KEY_J :: 74 // Key: J | j -KEY_K :: 75 // Key: K | k -KEY_L :: 76 // Key: L | l -KEY_M :: 77 // Key: M | m -KEY_N :: 78 // Key: N | n -KEY_O :: 79 // Key: O | o -KEY_P :: 80 // Key: P | p -KEY_Q :: 81 // Key: Q | q -KEY_R :: 82 // Key: R | r -KEY_S :: 83 // Key: S | s -KEY_T :: 84 // Key: T | t -KEY_U :: 85 // Key: U | u -KEY_V :: 86 // Key: V | v -KEY_W :: 87 // Key: W | w -KEY_X :: 88 // Key: X | x -KEY_Y :: 89 // Key: Y | y -KEY_Z :: 90 // Key: Z | z -KEY_LEFT_BRACKET :: 91 // Key: [ -KEY_BACKSLASH :: 92 // Key: '\' -KEY_RIGHT_BRACKET :: 93 // Key: ] -KEY_GRAVE :: 96 // Key: ` -// Function keys -KEY_SPACE :: 32 // Key: Space -KEY_ESCAPE :: 256 // Key: Esc -KEY_ENTER :: 257 // Key: Enter -KEY_TAB :: 258 // Key: Tab -KEY_BACKSPACE :: 259 // Key: Backspace -KEY_INSERT :: 260 // Key: Ins -KEY_DELETE :: 261 // Key: Del -KEY_RIGHT :: 262 // Key: Cursor right -KEY_LEFT :: 263 // Key: Cursor left -KEY_DOWN :: 264 // Key: Cursor down -KEY_UP :: 265 // Key: Cursor up -KEY_PAGE_UP :: 266 // Key: Page up -KEY_PAGE_DOWN :: 267 // Key: Page down -KEY_HOME :: 268 // Key: Home -KEY_END :: 269 // Key: End -KEY_CAPS_LOCK :: 280 // Key: Caps lock -KEY_SCROLL_LOCK :: 281 // Key: Scroll down -KEY_NUM_LOCK :: 282 // Key: Num lock -KEY_PRINT_SCREEN :: 283 // Key: Print screen -KEY_PAUSE :: 284 // Key: Pause -KEY_F1 :: 290 // Key: F1 -KEY_F2 :: 291 // Key: F2 -KEY_F3 :: 292 // Key: F3 -KEY_F4 :: 293 // Key: F4 -KEY_F5 :: 294 // Key: F5 -KEY_F6 :: 295 // Key: F6 -KEY_F7 :: 296 // Key: F7 -KEY_F8 :: 297 // Key: F8 -KEY_F9 :: 298 // Key: F9 -KEY_F10 :: 299 // Key: F10 -KEY_F11 :: 300 // Key: F11 -KEY_F12 :: 301 // Key: F12 -KEY_LEFT_SHIFT :: 340 // Key: Shift left -KEY_LEFT_CONTROL :: 341 // Key: Control left -KEY_LEFT_ALT :: 342 // Key: Alt left -KEY_LEFT_SUPER :: 343 // Key: Super left -KEY_RIGHT_SHIFT :: 344 // Key: Shift right -KEY_RIGHT_CONTROL :: 345 // Key: Control right -KEY_RIGHT_ALT :: 346 // Key: Alt right -KEY_RIGHT_SUPER :: 347 // Key: Super right -KEY_KB_MENU :: 348 // Key: KB menu -// Keypad keys -KEY_KP_0 :: 320 // Key: Keypad 0 -KEY_KP_1 :: 321 // Key: Keypad 1 -KEY_KP_2 :: 322 // Key: Keypad 2 -KEY_KP_3 :: 323 // Key: Keypad 3 -KEY_KP_4 :: 324 // Key: Keypad 4 -KEY_KP_5 :: 325 // Key: Keypad 5 -KEY_KP_6 :: 326 // Key: Keypad 6 -KEY_KP_7 :: 327 // Key: Keypad 7 -KEY_KP_8 :: 328 // Key: Keypad 8 -KEY_KP_9 :: 329 // Key: Keypad 9 -KEY_KP_DECIMAL :: 330 // Key: Keypad . -KEY_KP_DIVIDE :: 331 // Key: Keypad / -KEY_KP_MULTIPLY :: 332 // Key: Keypad * -KEY_KP_SUBTRACT :: 333 // Key: Keypad - -KEY_KP_ADD :: 334 // Key: Keypad + -KEY_KP_ENTER :: 335 // Key: Keypad Enter -KEY_KP_EQUAL :: 336 // Key: Keypad :: -// Android key buttons -KEY_BACK :: 4 // Key: Android back button -KEY_MENU :: 82 // Key: Android menu button -KEY_VOLUME_UP :: 24 // Key: Android volume up button -KEY_VOLUME_DOWN :: 25 // Key: Android volume down button - - -MOUSE_BUTTON_LEFT :: 0 // Mouse button left -MOUSE_BUTTON_RIGHT :: 1 // Mouse button right -MOUSE_BUTTON_MIDDLE :: 2 // Mouse button middle (pressed wheel) -MOUSE_BUTTON_SIDE :: 3 // Mouse button side (advanced mouse device) -MOUSE_BUTTON_EXTRA :: 4 // Mouse button extra (advanced mouse device) -MOUSE_BUTTON_FORWARD :: 5 // Mouse button forward (advanced mouse device) -MOUSE_BUTTON_BACK :: 6 // Mouse button back (advanced mouse device) - - -InitWindow :: #foreign (width: int, height: int, title: *char) // Initialize window and OpenGL context -WindowShouldClose :: #foreign (): bool // Check if KEY_ESCAPE pressed or Close icon pressed -CloseWindow :: #foreign () // Close window and unload OpenGL context -IsWindowReady :: #foreign (): bool // Check if window has been initialized successfully -IsWindowFullscreen :: #foreign (): bool // Check if window is currently fullscreen -IsWindowHidden :: #foreign (): bool // Check if window is currently hidden (only PLATFORM_DESKTOP) -IsWindowMinimized :: #foreign (): bool // Check if window is currently minimized (only PLATFORM_DESKTOP) -IsWindowMaximized :: #foreign (): bool // Check if window is currently maximized (only PLATFORM_DESKTOP) -IsWindowFocused :: #foreign (): bool // Check if window is currently focused (only PLATFORM_DESKTOP) -IsWindowResized :: #foreign (): bool // Check if window has been resized last frame -IsWindowState :: #foreign (flag: ConfigFlag): bool // Check if one specific window flag is enabled -SetWindowState :: #foreign (flags: ConfigFlags) // Set window configuration state using flags (only PLATFORM_DESKTOP) -ClearWindowState :: #foreign (flags: ConfigFlags) // Clear window configuration state flags -ToggleFullscreen :: #foreign () // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) -MaximizeWindow :: #foreign () // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) -MinimizeWindow :: #foreign () // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) -RestoreWindow :: #foreign () // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) -SetWindowIcon :: #foreign (image: Image) // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) -SetWindowIcons :: #foreign (images: *Image, count: int) // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) -SetWindowTitle :: #foreign (title: *char) // Set title for window (only PLATFORM_DESKTOP) -SetWindowPosition :: #foreign (x: int, y: int) // Set window position on screen (only PLATFORM_DESKTOP) -SetWindowMonitor :: #foreign (monitor: int) // Set monitor for the current window (fullscreen mode) -SetWindowMinSize :: #foreign (width: int, height: int) // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) -SetWindowSize :: #foreign (width: int, height: int) // Set window dimensions -SetWindowOpacity :: #foreign (opacity: F32) // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) -GetWindowHandle :: #foreign (): *void // Get native window handle -GetScreenWidth :: #foreign (): int // Get current screen width -GetScreenHeight :: #foreign (): int // Get current screen height -GetRenderWidth :: #foreign (): int // Get current render width (it considers HiDPI) -GetRenderHeight :: #foreign (): int // Get current render height (it considers HiDPI) -GetMonitorCount :: #foreign (): int // Get number of connected monitors -GetCurrentMonitor :: #foreign (): int // Get current connected monitor -GetMonitorPosition :: #foreign (monitor: int): Vector2 // Get specified monitor position -GetMonitorWidth :: #foreign (monitor: int): int // Get specified monitor width (current video mode used by monitor) -GetMonitorHeight :: #foreign (monitor: int): int // Get specified monitor height (current video mode used by monitor) -GetMonitorPhysicalWidth :: #foreign (monitor: int): int // Get specified monitor physical width in millimetres -GetMonitorPhysicalHeight :: #foreign (monitor: int): int // Get specified monitor physical height in millimetres -GetMonitorRefreshRate :: #foreign (monitor: int): int // Get specified monitor refresh rate -GetWindowPosition :: #foreign (): Vector2 // Get window position XY on monitor -GetWindowScaleDPI :: #foreign (): Vector2 // Get window scale DPI factor -GetMonitorName :: #foreign (monitor: int): *char // Get the human-readable, UTF-8 encoded name of the primary monitor -SetClipboardText :: #foreign (text: *char) // Set clipboard text content -GetClipboardText :: #foreign (): *char // Get clipboard text content -EnableEventWaiting :: #foreign () // Enable waiting for events on EndDrawing(), no automatic event polling -DisableEventWaiting :: #foreign () // Disable waiting for events on EndDrawing(), automatic events polling - - -// Custom frame control functions -// NOTE: Those functions are intended for advance users that want full control over the frame processing -// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents() -// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL - -SwapScreenBuffer :: #foreign () // Swap back buffer with front buffer (screen drawing) -PollInputEvents :: #foreign () // Register all input events -WaitTime :: #foreign (seconds: F64) // Wait for some time (halt program execution) - - -// Cursor-related functions - -ShowCursor :: #foreign () // Shows cursor -HideCursor :: #foreign () // Hides cursor -IsCursorHidden :: #foreign (): bool // Check if cursor is not visible -EnableCursor :: #foreign () // Enables cursor (unlock cursor) -DisableCursor :: #foreign () // Disables cursor (lock cursor) -IsCursorOnScreen :: #foreign (): bool // Check if cursor is on the current screen. - -// Drawing-related functions - -ClearBackground :: #foreign (color: Color) // Set background color (framebuffer clear color) -BeginDrawing :: #foreign () // Setup canvas (framebuffer) to start drawing -EndDrawing :: #foreign () // End canvas drawing and swap buffers (double buffering) -BeginMode2D :: #foreign (camera: Camera2D) // Initialize 2D mode with custom camera (2D) -EndMode2D :: #foreign () // Ends 2D mode with custom camera -BeginMode3D :: #foreign (camera: Camera3D) // Initializes 3D mode with custom camera (3D) -EndMode3D :: #foreign () // Ends 3D mode and returns to default 2D orthographic mode -BeginTextureMode :: #foreign (target: RenderTexture2D) // Initializes render texture for drawing -EndTextureMode :: #foreign () // Ends drawing to render texture -BeginShaderMode :: #foreign (shader: Shader) // Begin custom shader drawing -EndShaderMode :: #foreign () // End custom shader drawing (use default shader) -BeginBlendMode :: #foreign (mode: BlendMode) // Begin blending mode (alpha, additive, multiplied) -EndBlendMode :: #foreign () // End blending mode (reset to default: alpha blending) -BeginScissorMode :: #foreign (x: int, y: int, width: int, height: int) // Begin scissor mode (define screen area for following drawing) -EndScissorMode :: #foreign () // End scissor mode -BeginVrStereoMode :: #foreign (config: VrStereoConfig) // Begin stereo rendering (requires VR simulator) -EndVrStereoMode :: #foreign () // End stereo rendering (requires VR simulator) - -// VR stereo config functions for VR simulator - -// LoadVrStereoConfig :: #foreign (device: VrDeviceInfo): VrStereoConfig // Load VR stereo config for VR simulator device parameters -// UnloadVrStereoConfig :: #foreign (config: VrStereoConfig) // Unload VR stereo config - -// Shader management functions -// NOTE: Shader functionality is not available on OpenGL 1.1 - -LoadShader :: #foreign (vsFileName: *char, fsFileName: *char): Shader // Load shader from files and bind default locations -LoadShaderFromMemory :: #foreign (vsCode: *char, fsCode: *char): Shader // Load shader from code strings and bind default locations -IsShaderReady :: #foreign (shader: Shader): bool // Check if a shader is ready -GetShaderLocation :: #foreign (shader: Shader, uniformName: *char): int // Get shader uniform location -GetShaderLocationAttrib :: #foreign (shader: Shader, attribName: *char): int // Get shader attribute location -SetShaderValue :: #foreign (shader: Shader, locIndex: ShaderLocationIndex, value: *void, uniformType: ShaderUniformDataType) // Set shader uniform value -SetShaderValueV :: #foreign (shader: Shader, locIndex: ShaderLocationIndex, value: *void, uniformType: ShaderUniformDataType, count: int) // Set shader uniform value vector -SetShaderValueMatrix :: #foreign (shader: Shader, locIndex: ShaderLocationIndex, mat: Matrix) // Set shader uniform value (matrix 4x4) -SetShaderValueTexture :: #foreign (shader: Shader, locIndex: ShaderLocationIndex, texture: Texture2D) // Set shader uniform value for texture (sampler2d) -UnloadShader :: #foreign (shader: Shader) // Unload shader from GPU memory (VRAM) - -// Screen-space-related functions - -GetMouseRay :: #foreign (mousePosition: Vector2, camera: Camera): Ray // Get a ray trace from mouse position -GetCameraMatrix :: #foreign (camera: Camera): Matrix // Get camera transform matrix (view matrix) -GetCameraMatrix2D :: #foreign (camera: Camera2D): Matrix // Get camera 2d transform matrix -GetWorldToScreen :: #foreign (position: Vector3, camera: Camera): Vector2 // Get the screen space position for a 3d world space position -GetScreenToWorld2D :: #foreign (position: Vector2, camera: Camera2D): Vector2 // Get the world space position for a 2d camera screen space position -GetWorldToScreenEx :: #foreign (position: Vector3, camera: Camera, width: int, height: int): Vector2 // Get size position for a 3d world space position -GetWorldToScreen2D :: #foreign (position: Vector2, camera: Camera2D): Vector2 // Get the screen space position for a 2d camera world space position - -// Timing-related functions - -SetTargetFPS :: #foreign (fps: int) // Set target FPS (maximum) -GetFPS :: #foreign (): int // Returns current FPS -GetFrameTime :: #foreign (): F32 // Returns time in seconds for last frame drawn (delta time) -GetTime :: #foreign (): F64 // Returns elapsed time in seconds since InitWindow() - -// Misc. functions - -GetRandomValue :: #foreign (min: int, max: int): int // Returns a random value between min and max (both included) -SetRandomSeed :: #foreign (seed: uint) // Set the seed for the random number generator -TakeScreenshot :: #foreign (fileName: *char) // Takes a screenshot of current screen (filename extension defines format) -SetConfigFlags :: #foreign (flags: ConfigFlags) // Setup init configuration flags (view FLAGS) - -TraceLog :: #foreign (logLevel: TraceLogLevel, text: *char, ...) // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR) -SetTraceLogLevel :: #foreign (logLevel: TraceLogLevel) // Set the current threshold (minimum) log level -MemAlloc :: #foreign (size: uint): *void // Internal memory allocator -MemRealloc :: #foreign (ptr: *void, size: uint): *void // Internal memory reallocator -MemFree :: #foreign (ptr: *void) // Internal memory free - -OpenURL :: #foreign (url: *char) // Open URL with default system browser (if available) - -// Set custom callbacks -// WARNING: Callbacks setup is intended for advance users - -SetTraceLogCallback :: #foreign (callback: TraceLogCallback) // Set custom trace log -SetLoadFileDataCallback :: #foreign (callback: LoadFileDataCallback) // Set custom file binary data loader -SetSaveFileDataCallback :: #foreign (callback: SaveFileDataCallback) // Set custom file binary data saver -SetLoadFileTextCallback :: #foreign (callback: LoadFileTextCallback) // Set custom file text data loader -SetSaveFileTextCallback :: #foreign (callback: SaveFileTextCallback) // Set custom file text data saver - -// Files management functions - -LoadFileData :: #foreign (fileName: *char, bytesRead: *uint): *uchar // Load file data as byte array (read) -UnloadFileData :: #foreign (data: *uchar) // Unload file data allocated by LoadFileData() -SaveFileData :: #foreign (fileName: *char, data: *void, bytesToWrite: uint): bool // Save data to file from byte array (write), returns true on success -ExportDataAsCode :: #foreign (data: *void, size: uint, fileName: *char): bool // Export data to code (.h), returns true on success -LoadFileText :: #foreign (fileName: *char): *uchar // Load text data from file (read), returns a '\0' terminated string -UnloadFileText :: #foreign (text: *uchar) // Unload file text data allocated by LoadFileText() -SaveFileText :: #foreign (fileName: *char, text: *uchar): bool // Save text data to file (write), string must be '\0' terminated, returns true on success -FileExists :: #foreign (fileName: *char): bool // Check if file exists -DirectoryExists :: #foreign (dirPath: *char): bool // Check if a directory path exists -IsFileExtension :: #foreign (fileName: *char, ext: *char): bool // Check file extension (including point: .png, .wav) -GetFileLength :: #foreign (fileName: *char): int // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) -GetFileExtension :: #foreign (fileName: *char): *char // Get pointer to extension for a filename string (includes dot: '.png') -GetFileName :: #foreign (filePath: *char): *char // Get pointer to filename for a path string -GetFileNameWithoutExt :: #foreign (filePath: *char): *char // Get filename string without extension (uses static string) -GetDirectoryPath :: #foreign (filePath: *char): *char // Get full path for a given fileName with path (uses static string) -GetPrevDirectoryPath :: #foreign (dirPath: *char): *char // Get previous directory path for a given path (uses static string) -GetWorkingDirectory :: #foreign (): *char // Get current working directory (uses static string) -GetApplicationDirectory :: #foreign (): *char // Get the directory if the running application (uses static string) -ChangeDirectory :: #foreign (dir: *char): bool // Change working directory, return true on success -IsPathFile :: #foreign (path: *char): bool // Check if a given path is a file or a directory -LoadDirectoryFiles :: #foreign (dirPath: *char): FilePathList // Load directory filepaths -LoadDirectoryFilesEx :: #foreign (basePath: *char, filter: *char, scanSubdirs: bool): FilePathList // Load directory filepaths with extension filtering and recursive directory scan -UnloadDirectoryFiles :: #foreign (files: FilePathList) // Unload filepaths -IsFileDropped :: #foreign (): bool // Check if a file has been dropped into window -LoadDroppedFiles :: #foreign (): FilePathList // Load dropped filepaths -UnloadDroppedFiles :: #foreign (files: FilePathList) // Unload dropped filepaths -GetFileModTime :: #foreign (fileName: *char): long // Get file modification time (last write time) - -// Compression/Encoding functionality - -CompressData :: #foreign (data: *void, dataSize: int, compDataSize: *int): *uchar // Compress data (DEFLATE algorithm), memory must be MemFree() -DecompressData :: #foreign (compData: *void, compDataSize: int, dataSize: *int): *uchar // Decompress data (DEFLATE algorithm), memory must be MemFree() -EncodeDataBase64 :: #foreign (data: *void, dataSize: int, outputSize: *int): *uchar // Encode data to Base64 string, memory must be MemFree() -DecodeDataBase64 :: #foreign (data: *void, outputSize: *int): *uchar // Decode Base64 string data, memory must be MemFree() - -//------------------------------------------------------------------------------------ -// Input Handling Functions (Module: core) -//------------------------------------------------------------------------------------ - -// Input-related functions: keyboard - -IsKeyPressed :: #foreign (key: int): bool // Detect if a key has been pressed once -IsKeyDown :: #foreign (key: int): bool // Detect if a key is being pressed -IsKeyReleased :: #foreign (key: int): bool // Detect if a key has been released once -IsKeyUp :: #foreign (key: int): bool // Detect if a key is NOT being pressed -SetExitKey :: #foreign (key: int) // Set a custom key to exit program (default is ESC) -GetKeyPressed :: #foreign (): int // Get key pressed (keycode), call it multiple times for keys queued -GetCharPressed :: #foreign (): rune // Get char pressed (unicode), call it multiple times for chars queued - -// Input-related functions: gamepads - -IsGamepadAvailable :: #foreign (gamepad: int): bool // Check if a gamepad is available -GetGamepadName :: #foreign (gamepad: int): *char // Get gamepad internal name id -IsGamepadButtonPressed :: #foreign (gamepad: int, button: GamepadButton): bool // Check if a gamepad button has been pressed once -IsGamepadButtonDown :: #foreign (gamepad: int, button: GamepadButton): bool // Check if a gamepad button is being pressed -IsGamepadButtonReleased :: #foreign (gamepad: int, button: GamepadButton): bool // Check if a gamepad button has been released once -IsGamepadButtonUp :: #foreign (gamepad: int, button: GamepadButton): bool // Check if a gamepad button is NOT being pressed -GetGamepadButtonPressed :: #foreign (): GamepadButton // Get the last gamepad button pressed -GetGamepadAxisCount :: #foreign (gamepad: int): int // Get gamepad axis count for a gamepad -GetGamepadAxisMovement :: #foreign (gamepad: int, axis: GamepadAxis): F32 // Get axis movement value for a gamepad axis -SetGamepadMappings :: #foreign (mappings: *char): int // Set internal gamepad mappings (SDL_GameControllerDB) - -// Input-related functions: mouse - -IsMouseButtonPressed :: #foreign (button: int): bool // Detect if a mouse button has been pressed once -IsMouseButtonDown :: #foreign (button: int): bool // Detect if a mouse button is being pressed -IsMouseButtonReleased :: #foreign (button: int): bool // Detect if a mouse button has been released once -IsMouseButtonUp :: #foreign (button: int): bool // Detect if a mouse button is NOT being pressed -GetMouseX :: #foreign (): int // Returns mouse position X -GetMouseY :: #foreign (): int // Returns mouse position Y -GetMousePosition :: #foreign (): Vector2 // Returns mouse position XY -GetMouseDelta :: #foreign (): Vector2 // Returns mouse delta XY -SetMousePosition :: #foreign (x: int, y: int) // Set mouse position XY -SetMouseOffset :: #foreign (offsetX: int, offsetY: int) // Set mouse offset -SetMouseScale :: #foreign (scaleX: F32, scaleY: F32) // Set mouse scaling -GetMouseWheelMove :: #foreign (): F32 // Returns mouse wheel movement Y -GetMouseWheelMoveV :: #foreign (): Vector2 // Get mouse wheel movement for both X and Y -SetMouseCursor :: #foreign (cursor: MouseCursor) // Set mouse cursor - -// Input-related functions: touch - -GetTouchX :: #foreign (): int // Returns touch position X for touch point 0 (relative to screen size) -GetTouchY :: #foreign (): int // Returns touch position Y for touch point 0 (relative to screen size) -GetTouchPosition :: #foreign (index: int): Vector2 // Returns touch position XY for a touch point index (relative to screen size) -GetTouchPointId :: #foreign (index: int): int // Get touch point identifier for given index -GetTouchPointCount :: #foreign (): int // Get number of touch points - -//------------------------------------------------------------------------------------ -// Gestures and Touch Handling Functions (Module: rgestures) -//------------------------------------------------------------------------------------ - -SetGesturesEnabled :: #foreign (flags: Gestures) // Enable a set of gestures using flags -IsGestureDetected :: #foreign (gesture: Gesture): bool // Check if a gesture have been detected -GetGestureDetected :: #foreign (): Gesture // Get latest detected gesture -GetGestureHoldDuration :: #foreign (): F32 // Get gesture hold time in milliseconds -GetGestureDragVector :: #foreign (): Vector2 // Get gesture drag vector -GetGestureDragAngle :: #foreign (): F32 // Get gesture drag angle -GetGesturePinchVector :: #foreign (): Vector2 // Get gesture pinch delta -GetGesturePinchAngle :: #foreign (): F32 // Get gesture pinch angle - -//------------------------------------------------------------------------------------ -// Camera System Functions (Module: camera) -//------------------------------------------------------------------------------------ - -UpdateCamera :: #foreign (camera: *Camera, mode: CameraMode) // Set camera mode (multiple camera modes available) -UpdateCameraPro :: #foreign (camera: *Camera, movement: Vector3, rotation: Vector3, zoom: F32) // Update camera movement/rotation - -//------------------------------------------------------------------------------------ -// Basic Shapes Drawing Functions (Module: shapes) -//------------------------------------------------------------------------------------ -// Set texture and rectangle to be used on shapes drawing -// NOTE: It can be useful when using basic shapes and one single font, -// defining a font char white rectangle would allow drawing everything in a single draw call - -SetShapesTexture :: #foreign (texture: Texture2D, source: Rectangle) - -// Basic shapes drawing functions - -DrawPixel :: #foreign (posX: int, posY: int, color: Color) // Draw a pixel -DrawPixelV :: #foreign (position: Vector2, color: Color) // Draw a pixel (Vector version) -DrawLine :: #foreign (startPosX: int, startPosY: int, endPosX: int, endPosY: int, color: Color) // Draw a line -DrawLineV :: #foreign (startPos: Vector2, endPos: Vector2, color: Color) // Draw a line (Vector version) -DrawLineEx :: #foreign (startPos: Vector2, endPos: Vector2, thick: F32, color: Color) // Draw a line defining thickness -DrawLineBezier :: #foreign (startPos: Vector2, endPos: Vector2, thick: F32, color: Color) // Draw a line using cubic-bezier curves in-out -DrawLineBezierQuad :: #foreign (startPos: Vector2, endPos: Vector2, controlPos: Vector2, thick: F32, color: Color) // Draw line using quadratic bezier curves with a control point -DrawLineBezierCubic :: #foreign (startPos: Vector2, endPos: Vector2, startControlPos: Vector2, endControlPos: Vector2, thick: F32, color: Color) // Draw line using cubic bezier curves with 2 control points -DrawLineStrip :: #foreign (points: *Vector2, pointCount: int, color: Color) // Draw lines sequence -DrawCircle :: #foreign (centerX: int, centerY: int, radius: F32, color: Color) // Draw a color-filled circle -DrawCircleSector :: #foreign (center: Vector2, radius: F32, startAngle: F32, endAngle: F32, segments: int, color: Color) // Draw a piece of a circle -DrawCircleSectorLines :: #foreign (center: Vector2, radius: F32, startAngle: F32, endAngle: F32, segments: int, color: Color) // Draw circle sector outline -DrawCircleGradient :: #foreign (centerX: int, centerY: int, radius: F32, color1: Color, color2: Color) // Draw a gradient-filled circle -DrawCircleV :: #foreign (center: Vector2, radius: F32, color: Color) // Draw a color-filled circle (Vector version) -DrawCircleLines :: #foreign (centerX: int, centerY: int, radius: F32, color: Color) // Draw circle outline -DrawEllipse :: #foreign (centerX: int, centerY: int, radiusH: F32, radiusV: F32, color: Color) // Draw ellipse -DrawEllipseLines :: #foreign (centerX: int, centerY: int, radiusH: F32, radiusV: F32, color: Color) // Draw ellipse outline -DrawRing :: #foreign (center: Vector2, innerRadius: F32, outerRadius: F32, startAngle: F32, endAngle: F32, segments: int, color: Color) // Draw ring -DrawRingLines :: #foreign (center: Vector2, innerRadius: F32, outerRadius: F32, startAngle: F32, endAngle: F32, segments: int, color: Color) // Draw ring outline -DrawRectangle :: #foreign (posX: int, posY: int, width: int, height: int, color: Color) // Draw a color-filled rectangle -DrawRectangleV :: #foreign (position: Vector2, size: Vector2, color: Color) // Draw a color-filled rectangle (Vector version) -DrawRectangleRec :: #foreign (rec: Rectangle, color: Color) // Draw a color-filled rectangle -DrawRectanglePro :: #foreign (rec: Rectangle, origin: Vector2, rotation: F32, color: Color) // Draw a color-filled rectangle with pro parameters -DrawRectangleGradientV :: #foreign (posX: int, posY: int, width: int, height: int, color1: Color, color2: Color) // Draw a vertical-gradient-filled rectangle -DrawRectangleGradientH :: #foreign (posX: int, posY: int, width: int, height: int, color1: Color, color2: Color) // Draw a horizontal-gradient-filled rectangle -DrawRectangleGradientEx :: #foreign (rec: Rectangle, col1: Color, col2: Color, col3: Color, col4: Color) // Draw a gradient-filled rectangle with custom vertex colors -DrawRectangleLines :: #foreign (posX: int, posY: int, width: int, height: int, color: Color) // Draw rectangle outline -DrawRectangleLinesEx :: #foreign (rec: Rectangle, lineThick: F32, color: Color) // Draw rectangle outline with extended parameters -DrawRectangleRounded :: #foreign (rec: Rectangle, roundness: F32, segments: int, color: Color) // Draw rectangle with rounded edges -DrawRectangleRoundedLines :: #foreign (rec: Rectangle, roundness: F32, segments: int, lineThick: F32, color: Color) // Draw rectangle with rounded edges outline -DrawTriangle :: #foreign (v1: Vector2, v2: Vector2, v3: Vector2, color: Color) // Draw a color-filled triangle (vertex in counter-clockwise order!) -DrawTriangleLines :: #foreign (v1: Vector2, v2: Vector2, v3: Vector2, color: Color) // Draw triangle outline (vertex in counter-clockwise order!) -DrawTriangleFan :: #foreign (points: *Vector2, pointCount: int, color: Color) // Draw a triangle fan defined by points (first vertex is the center) -DrawTriangleStrip :: #foreign (points: *Vector2, pointCount: int, color: Color) // Draw a triangle strip defined by points -DrawPoly :: #foreign (center: Vector2, sides: int, radius: F32, rotation: F32, color: Color) // Draw a regular polygon (Vector version) -DrawPolyLines :: #foreign (center: Vector2, sides: int, radius: F32, rotation: F32, color: Color) // Draw a polygon outline of n sides -DrawPolyLinesEx :: #foreign (center: Vector2, sides: int, radius: F32, rotation: F32, lineThick: F32, color: Color) // Draw a polygon outline of n sides with extended parameters - - // Basic shapes collision detection functions -CheckCollisionRecs :: #foreign (rec1: Rectangle, rec2: Rectangle): bool // Check collision between two rectangles -CheckCollisionCircles :: #foreign (center1: Vector2, radius1: F32, center2: Vector2, radius2: F32): bool // Check collision between two circles -CheckCollisionCircleRec :: #foreign (center: Vector2, radius: F32, rec: Rectangle): bool // Check collision between circle and rectangle -CheckCollisionPointRec :: #foreign (point: Vector2, rec: Rectangle): bool // Check if point is inside rectangle -CheckCollisionPointCircle :: #foreign (point: Vector2, center: Vector2, radius: F32): bool // Check if point is inside circle -CheckCollisionPointTriangle :: #foreign (point: Vector2, p1: Vector2, p2: Vector2, p3: Vector2): bool // Check if point is inside a triangle -CheckCollisionPointPoly :: #foreign (point: Vector2, points: *Vector2, pointCount: int): bool // Check if point is within a polygon described by array of vertices -CheckCollisionLines :: #foreign (startPos1: Vector2, endPos1: Vector2, startPos2: Vector2, endPos2: Vector2, collisionPoint: *Vector2): bool // Check the collision between two lines defined by two points each, returns collision point by reference -CheckCollisionPointLine :: #foreign (point: Vector2, p1: Vector2, p2: Vector2, threshold: int): bool // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] -GetCollisionRec :: #foreign (rec1: Rectangle, rec2: Rectangle): Rectangle // Get collision rectangle for two rectangles collision - - - -// Image loading functions -// NOTE: These functions do not require GPU access - -LoadImage :: #foreign (fileName: *char): Image // Load image from file into CPU memory (RAM) -LoadImageRaw :: #foreign (fileName: *char, width: int, height: int, format: PixelFormat, headerSize: int): Image // Load image from RAW file data -LoadImageAnim :: #foreign (fileName: *char, frames: *int): Image // Load image sequence from file (frames appended to image.data) -LoadImageFromMemory :: #foreign (fileType: *char, fileData: *void, dataSize: int): Image // Load image from memory buffer, fileType refers to extension: i.e. '.png' -LoadImageFromTexture :: #foreign (texture: Texture2D): Image // Load image from GPU texture data -LoadImageFromScreen :: #foreign (): Image // Load image from screen buffer and (screenshot) -IsImageReady :: #foreign (image: Image): bool // Check if an image is ready -UnloadImage :: #foreign (image: Image) // Unload image from CPU memory (RAM) -ExportImage :: #foreign (image: Image, fileName: *char): bool // Export image data to file, returns true on success -ExportImageAsCode :: #foreign (image: Image, fileName: *char): bool // Export image as code file defining an array of bytes, returns true on success - -// Image generation functions - -GenImageColor :: #foreign (width: int, height: int, color: Color): Image // Generate image: plain color -GenImageGradientV :: #foreign (width: int, height: int, top: Color, bottom: Color): Image // Generate image: vertical gradient -GenImageGradientH :: #foreign (width: int, height: int, left: Color, right: Color): Image // Generate image: horizontal gradient -GenImageGradientRadial :: #foreign (width: int, height: int, density: F32, inner: Color, outer: Color): Image // Generate image: radial gradient -GenImageChecked :: #foreign (width: int, height: int, checksX: int, checksY: int, col1: Color, col2: Color): Image // Generate image: checked -GenImageWhiteNoise :: #foreign (width: int, height: int, factor: F32): Image // Generate image: white noise -GenImagePerlinNoise :: #foreign (width: int, height: int, offsetX: int, offsetY: int, scale: F32): Image // Generate image: perlin noise -GenImageCellular :: #foreign (width: int, height: int, tileSize: int): Image // Generate image: cellular algorithm, bigger tileSize means bigger cells -GenImageText :: #foreign (width: int, height: int, text: *char): Image // Generate image: grayscale image from text data - -// Image manipulation functions - -ImageCopy :: #foreign (image: Image): Image // Create an image duplicate (useful for transformations) -ImageFromImage :: #foreign (image: Image, rec: Rectangle): Image // Create an image from another image piece -ImageText :: #foreign (text: *char, fontSize: int, color: Color): Image // Create an image from text (default font) -ImageTextEx :: #foreign (font: Font, text: *char, fontSize: F32, spacing: F32, tint: Color): Image // Create an image from text (custom sprite font) -ImageFormat :: #foreign (image: *Image, newFormat: PixelFormat) // Convert image data to desired format -ImageToPOT :: #foreign (image: *Image, fill: Color) // Convert image to POT (power-of-two) -ImageCrop :: #foreign (image: *Image, crop: Rectangle) // Crop an image to a defined rectangle -ImageAlphaCrop :: #foreign (image: *Image, threshold: F32) // Crop image depending on alpha value -ImageAlphaClear :: #foreign (image: *Image, color: Color, threshold: F32) // Clear alpha channel to desired color -ImageAlphaMask :: #foreign (image: *Image, alphaMask: Image) // Apply alpha mask to image -ImageAlphaPremultiply :: #foreign (image: *Image) // Premultiply alpha channel -ImageBlurGaussian :: #foreign (image: *Image, blurSize: int) // Apply Gaussian blur using a box blur approximation -ImageResize :: #foreign (image: *Image, newWidth: int, newHeight: int) // Resize image (Bicubic scaling algorithm) -ImageResizeNN :: #foreign (image: *Image, newWidth: int, newHeight: int) // Resize image (Nearest-Neighbor scaling algorithm) -ImageResizeCanvas :: #foreign (image: *Image, newWidth: int, newHeight: int, offsetX: int, offsetY: int, fill: Color) // Resize canvas and fill with color -ImageMipmaps :: #foreign (image: *Image) // Compute all mipmap levels for a provided image -ImageDither :: #foreign (image: *Image, rBpp: int, gBpp: int, bBpp: int, aBpp: int) // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) -ImageFlipVertical :: #foreign (image: *Image) // Flip image vertically -ImageFlipHorizontal :: #foreign (image: *Image) // Flip image horizontally -ImageRotateCW :: #foreign (image: *Image) // Rotate image clockwise 90deg -ImageRotateCCW :: #foreign (image: *Image) // Rotate image counter-clockwise 90deg -ImageColorTint :: #foreign (image: *Image, color: Color) // Modify image color: tint -ImageColorInvert :: #foreign (image: *Image) // Modify image color: invert -ImageColorGrayscale :: #foreign (image: *Image) // Modify image color: grayscale -ImageColorContrast :: #foreign (image: *Image, contrast: F32) // Modify image color: contrast (-100 to 100) -ImageColorBrightness :: #foreign (image: *Image, brightness: int) // Modify image color: brightness (-255 to 255) -ImageColorReplace :: #foreign (image: *Image, color: Color, replace: Color) // Modify image color: replace color -LoadImageColors :: #foreign (image: Image): *Color // Load color data from image as a Color array (RGBA - 32bit) -LoadImagePalette :: #foreign (image: Image, maxPaletteSize: int, colorCount: *int): *Color // Load colors palette from image as a Color array (RGBA - 32bit) -UnloadImageColors :: #foreign (colors: *Color) // Unload color data loaded with LoadImageColors() -UnloadImagePalette :: #foreign (colors: *Color) // Unload colors palette loaded with LoadImagePalette() -GetImageAlphaBorder :: #foreign (image: Image, threshold: F32): Rectangle // Get image alpha border rectangle -GetImageColor :: #foreign (image: Image, x: int, y: int): Color // Get image pixel color at (x, y) position - -// Image drawing functions -// NOTE: Image software-rendering functions (CPU) -ImageClearBackground :: #foreign (dst: *Image, color: Color) // Clear image background with given color -ImageDrawPixel :: #foreign (dst: *Image, posX: int, posY: int, color: Color) // Draw pixel within an image -ImageDrawPixelV :: #foreign (dst: *Image, position: Vector2, color: Color) // Draw pixel within an image (Vector version) -ImageDrawLine :: #foreign (dst: *Image, startPosX: int, startPosY: int, endPosX: int, endPosY: int, color: Color) // Draw line within an image -ImageDrawLineV :: #foreign (dst: *Image, start: Vector2, end: Vector2, color: Color) // Draw line within an image (Vector version) -ImageDrawCircle :: #foreign (dst: *Image, centerX: int, centerY: int, radius: int, color: Color) // Draw a filled circle within an image -ImageDrawCircleV :: #foreign (dst: *Image, center: Vector2, radius: int, color: Color) // Draw a filled circle within an image (Vector version) -ImageDrawCircleLines :: #foreign (dst: *Image, centerX: int, centerY: int, radius: int, color: Color) // Draw circle outline within an image -ImageDrawCircleLinesV :: #foreign (dst: *Image, center: Vector2, radius: int, color: Color) // Draw circle outline within an image (Vector version) -ImageDrawRectangle :: #foreign (dst: *Image, posX: int, posY: int, width: int, height: int, color: Color) // Draw rectangle within an image -ImageDrawRectangleV :: #foreign (dst: *Image, position: Vector2, size: Vector2, color: Color) // Draw rectangle within an image (Vector version) -ImageDrawRectangleRec :: #foreign (dst: *Image, rec: Rectangle, color: Color) // Draw rectangle within an image -ImageDrawRectangleLines :: #foreign (dst: *Image, rec: Rectangle, thick: int, color: Color) // Draw rectangle lines within an image -ImageDraw :: #foreign (dst: *Image, src: Image, srcRec: Rectangle, dstRec: Rectangle, tint: Color) // Draw a source image within a destination image (tint applied to source) -ImageDrawText :: #foreign (dst: *Image, text: *char, posX: int, posY: int, fontSize: int, color: Color) // Draw text (using default font) within an image (destination) -ImageDrawTextEx :: #foreign (dst: *Image, font: Font, text: *char, position: Vector2, fontSize: F32, spacing: F32, tint: Color) // Draw text (custom sprite font) within an image (destination) - -// Texture loading functions -// NOTE: These functions require GPU access - -LoadTexture :: #foreign (fileName: *char): Texture2D // Load texture from file into GPU memory (VRAM) -LoadTextureFromImage :: #foreign (image: Image): Texture2D // Load texture from image data -LoadTextureCubemap :: #foreign (image: Image, layout: CubemapLayout): TextureCubemap // Load cubemap from image, multiple image cubemap layouts supported -LoadRenderTexture :: #foreign (width: int, height: int): RenderTexture2D // Load texture for rendering (framebuffer) -IsTextureReady :: #foreign (texture: Texture2D): bool // Check if a texture is ready -UnloadTexture :: #foreign (texture: Texture2D) // Unload texture from GPU memory (VRAM) -IsRenderTextureReady :: #foreign (target: RenderTexture2D): bool // Check if a render texture is ready -UnloadRenderTexture :: #foreign (target: RenderTexture2D) // Unload render texture from GPU memory (VRAM) -UpdateTexture :: #foreign (texture: Texture2D, pixels: *void) // Update GPU texture with new data -UpdateTextureRec :: #foreign (texture: Texture2D, rec: Rectangle, pixels: *void) // Update GPU texture rectangle with new data - -// Texture configuration functions - -GenTextureMipmaps :: #foreign (texture: *Texture2D) // Generate GPU mipmaps for a texture -SetTextureFilter :: #foreign (texture: Texture2D, filter: TextureFilter) // Set texture scaling filter mode -SetTextureWrap :: #foreign (texture: Texture2D, wrap: TextureWrap) // Set texture wrapping mode - - // Texture drawing functions -DrawTexture :: #foreign (texture: Texture2D, posX: int, posY: int, tint: Color) // Draw a Texture2D -DrawTextureV :: #foreign (texture: Texture2D, position: Vector2, tint: Color) // Draw a Texture2D with position defined as Vector2 -DrawTextureEx :: #foreign (texture: Texture2D, position: Vector2, rotation: F32, scale: F32, tint: Color) // Draw a Texture2D with extended parameters -DrawTextureRec :: #foreign (texture: Texture2D, source: Rectangle, position: Vector2, tint: Color) // Draw a part of a texture defined by a rectangle -DrawTexturePro :: #foreign (texture: Texture2D, source: Rectangle, dest: Rectangle, origin: Vector2, rotation: F32, tint: Color) // Draw a part of a texture defined by a rectangle with 'pro' parameters -DrawTextureNPatch :: #foreign (texture: Texture2D, nPatchInfo: NPatchInfo, dest: Rectangle, origin: Vector2, rotation: F32, tint: Color) // Draws a texture (or part of it) that stretches or shrinks nicely - -// Color/pixel related functions - -Fade :: #foreign (color: Color, alpha: F32): Color // Get color with alpha applied, alpha goes from 0.0f to 1.0f -ColorToInt :: #foreign (color: Color): uint // Get hexadecimal value for a Color -ColorNormalize :: #foreign (color: Color): Vector4 // Get Color normalized as float [0..1] -ColorFromNormalized :: #foreign (normalized: Vector4): Color // Get Color from normalized values [0..1] -ColorToHSV :: #foreign (color: Color): Vector3 // Get HSV values for a Color, hue [0..360], saturation/value [0..1] -ColorFromHSV :: #foreign (hue: F32, saturation: F32, value: F32): Color // Get a Color from HSV values, hue [0..360], saturation/value [0..1] -ColorTint :: #foreign (color: Color, tint: Color): Color // Get color multiplied with another color -ColorBrightness :: #foreign (color: Color, factor: F32): Color // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f -ColorContrast :: #foreign (color: Color, contrast: F32): Color // Get color with contrast correction, contrast values between -1.0f and 1.0f -ColorAlpha :: #foreign (color: Color, alpha: F32): Color // Get color with alpha applied, alpha goes from 0.0f to 1.0f -ColorAlphaBlend :: #foreign (dst: Color, src: Color, tint: Color): Color // Get src alpha-blended into dst color with tint -GetColor :: #foreign (hexValue: uint): Color // Get Color structure from hexadecimal value -GetPixelColor :: #foreign (srcPtr: *void, format: PixelFormat): Color // Get Color from a source pixel pointer of certain format -SetPixelColor :: #foreign (dstPtr: *void, color: Color, format: PixelFormat) // Set color formatted into destination pixel pointer -GetPixelDataSize :: #foreign (width: inr, height: int, format: PixelFormat): int // Get pixel data size in bytes for certain format - - - - -//------------------------------------------------------------------------------------ -// Font Loading and Text Drawing Functions (Module: text) -//------------------------------------------------------------------------------------ - -// Font loading/unloading functions - -GetFontDefault :: #foreign (): Font // Get the default Font -LoadFont :: #foreign (fileName: *char): Font // Load font from file into GPU memory (VRAM) -LoadFontEx :: #foreign (fileName: *char, fontSize: int, fontChars: *rune, glyphCount: int): Font // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set -LoadFontFromImage :: #foreign (image: Image, key: Color, firstChar: rune): Font // Load font from Image (XNA style) -LoadFontFromMemory :: #foreign (fileType: *char, fileData: *void, dataSize: int, fontSize: int, fontChars: *rune, glyphCount: int): Font // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' -IsFontReady :: #foreign (font: Font): bool // Check if a font is ready -LoadFontData :: #foreign (fileData: *void, dataSize: int, fontSize: int, fontChars: *rune, glyphCount: int, type: FontType): *GlyphInfo // Load font data for further use -GenImageFontAtlas :: #foreign (chars: *GlyphInfo, recs: **Rectangle, glyphCount: int, fontSize: int, padding: int, packMethod: int): Image // Generate image font atlas using chars info -UnloadFontData :: #foreign (chars: *GlyphInfo, glyphCount: int) // Unload font chars info data (RAM) -UnloadFont :: #foreign (font: Font) // Unload font from GPU memory (VRAM) -ExportFontAsCode :: #foreign (font: Font, fileName: *char): bool // Export font as code file, returns true on success - -// Text drawing functions - -DrawFPS :: #foreign (posX: int, posY: int) // Draw current FPS -DrawText :: #foreign (text: *char, posX: int, posY: int, fontSize: int, color: Color) // Draw text (using default font) -DrawTextEx :: #foreign (font: Font, text: *char, position: Vector2, fontSize: F32, spacing: F32, tint: Color) // Draw text using font and additional parameters -DrawTextPro :: #foreign (font: Font, text: *char, position: Vector2, origin: Vector2, rotation: F32, fontSize: F32, spacing: F32, tint: Color) // Draw text using Font and pro parameters (rotation) -DrawTextCodepoint :: #foreign (font: Font, codepoint: rune, position: Vector2, fontSize: F32, tint: Color) // Draw one character (codepoint) -DrawTextCodepoints :: #foreign (font: Font, codepoints: *rune, count: int, position: Vector2, fontSize: F32, spacing: F32, tint: Color) // Draw multiple character (codepoint) - -// Text font info functions - -MeasureText :: #foreign (text: *char, fontSize: int): int // Measure string width for default font -MeasureTextEx :: #foreign (font: Font, text: *char, fontSize: F32, spacing: F32): Vector2 // Measure string size for Font -GetGlyphIndex :: #foreign (font: Font, codepoint: rune): int // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found -GetGlyphInfo :: #foreign (font: Font, codepoint: rune): GlyphInfo // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found -GetGlyphAtlasRec :: #foreign (font: Font, codepoint: rune): Rectangle // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found - -// Text codepoints management functions (unicode characters) - -LoadUTF8 :: #foreign (codepoints: *rune, length: int): *uchar // Load UTF-8 text encoded from codepoints array -UnloadUTF8 :: #foreign (text: *uchar) // Unload UTF-8 text encoded from codepoints array -LoadCodepoints :: #foreign (text: *void, count: *int): *rune // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter -UnloadCodepoints :: #foreign (codepoints: *rune) // Unload codepoints data from memory -GetCodepointCount :: #foreign (text : *char): int // Get total number of codepoints in a UTF-8 encoded string -GetCodepoint :: #foreign (text: *char, codepointSize: *int): rune // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -GetCodepointNext :: #foreign (text: *char, codepointSize: *int): rune // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -GetCodepointPrevious :: #foreign (text: *char, codepointSize: *int): rune // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -CodepointToUTF8 :: #foreign (codepoint: rune, utf8Size: *int): *char // Encode one codepoint into UTF-8 byte array (array length returned as parameter) - -// Text strings management functions (no UTF-8 strings, only byte chars) -// NOTE: Some strings allocate memory internally for returned strings, just be careful! - -TextCopy :: #foreign (dst: *uchar, src: *char): int // Copy one string to another, returns bytes copied -TextIsEqual :: #foreign (text1: *char, text2: *char): bool // Check if two text string are equal -TextLength :: #foreign (text: *char): uint // Get text length, checks for '\0' ending - -// TextFormat is defined at the bottom of this file - -TextFormat :: #foreign (str: *char, ...): *char -TextSubtext :: #foreign (text: *char, position: int, length: int): *char // Get a piece of a text string -TextReplace :: #foreign (text: *uchar, replace: *char, by: *char): *uchar // Replace text string (WARNING: memory must be freed!) -TextInsert :: #foreign (text: *char, insert: *char, position: int): *uchar // Insert text in a position (WARNING: memory must be freed!) -TextJoin :: #foreign (textList: **char, count: int, delimiter: *char): *char // Join text strings with delimiter -TextSplit :: #foreign (text: *char, delimiter: byte, count: *int): **char // Split text into multiple strings -TextAppend :: #foreign (text: *uchar, append: *char, position: *int) // Append text at specific position and move cursor! -TextFindIndex :: #foreign (text: *char, find: *char): int // Find first text occurrence within a string -TextToUpper :: #foreign (text: *char): *char // Get upper case version of provided string -TextToLower :: #foreign (text: *char): *char // Get lower case version of provided string -TextToPascal :: #foreign (text: *char): *char // Get Pascal case notation version of provided string -TextToInteger :: #foreign (text: *char): int // Get integer value from text (negative values not supported) - - -//------------------------------------------------------------------------------------ -// Basic 3d Shapes Drawing Functions (Module: models) -//------------------------------------------------------------------------------------ - -// Basic geometric 3D shapes drawing functions - -DrawLine3D :: #foreign (startPos: Vector3, endPos: Vector3, color: Color) // Draw a line in 3D world space -DrawPoint3D :: #foreign (position: Vector3, color: Color) // Draw a point in 3D space, actually a small line -DrawCircle3D :: #foreign (center: Vector3, radius: F32, rotationAxis: Vector3, rotationAngle: F32, color: Color) // Draw a circle in 3D world space -DrawTriangle3D :: #foreign (v1: Vector3, v2: Vector3, v3: Vector3, color: Color) // Draw a color-filled triangle (vertex in counter-clockwise order!) -DrawTriangleStrip3D :: #foreign (points: *Vector3, pointCount: int, color: Color) // Draw a triangle strip defined by points -DrawCube :: #foreign (position: Vector3, width: F32, height: F32, length: F32, color: Color) // Draw cube -DrawCubeV :: #foreign (position: Vector3, size: Vector3, color: Color) // Draw cube (Vector version) -DrawCubeWires :: #foreign (position: Vector3, width: F32, height: F32, length: F32, color: Color) // Draw cube wires -DrawCubeWiresV :: #foreign (position: Vector3, size: Vector3, color: Color) // Draw cube wires (Vector version) -DrawSphere :: #foreign (centerPos: Vector3, radius: F32, color: Color) // Draw sphere -DrawSphereEx :: #foreign (centerPos: Vector3, radius: F32, rings: int, slices: int, color: Color) // Draw sphere with extended parameters -DrawSphereWires :: #foreign (centerPos: Vector3, radius: F32, rings: int, slices: int, color: Color) // Draw sphere wires -DrawCylinder :: #foreign (position: Vector3, radiusTop: F32, radiusBottom: F32, height: F32, slices: int, color: Color) // Draw a cylinder/cone -DrawCylinderEx :: #foreign (startPos: Vector3, endPos: Vector3, startRadius: F32, endRadius: F32, sides: int, color: Color) // Draw a cylinder with base at startPos and top at endPos -DrawCylinderWires :: #foreign (position: Vector3, radiusTop: F32, radiusBottom: F32, height: F32, slices: int, color: Color) // Draw a cylinder/cone wires -DrawCylinderWiresEx :: #foreign (startPos: Vector3, endPos: Vector3, startRadius: F32, endRadius: F32, sides: int, color: Color) // Draw a cylinder wires with base at startPos and top at endPos -DrawCapsule :: #foreign (startPos: Vector3, endPos: Vector3, radius: F32, slices: int, rings: int, color: Color) // Draw a capsule with the center of its sphere caps at startPos and endPos -DrawCapsuleWires :: #foreign (startPos: Vector3, endPos: Vector3, radius: F32, slices: int, rings: int, color: Color) // Draw capsule wireframe with the center of its sphere caps at startPos and endPos -DrawPlane :: #foreign (centerPos: Vector3, size: Vector2, color: Color) // Draw a plane XZ -DrawRay :: #foreign (ray: Ray, color: Color) // Draw a ray line -DrawGrid :: #foreign (slices: int, spacing: F32) // Draw a grid (centered at (0, 0, 0)) - -//------------------------------------------------------------------------------------ -// Model 3d Loading and Drawing Functions (Module: models) -//------------------------------------------------------------------------------------ - -// Model management functions - -LoadModel :: #foreign (fileName: *char): Model // Load model from files (meshes and materials) -LoadModelFromMesh :: #foreign (mesh: Mesh): Model // Load model from generated mesh (default material) -IsModelReady :: #foreign (model: Model): bool // Check if a model is ready -UnloadModel :: #foreign (model: Model) // Unload model (including meshes) from memory (RAM and/or VRAM) -GetModelBoundingBox :: #foreign (model: Model): BoundingBox // Compute model bounding box limits (considers all meshes) - -// Model drawing functions - -DrawModel :: #foreign (model: Model, position: Vector3, scale: F32, tint: Color) // Draw a model (with texture if set) -DrawModelEx :: #foreign (model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: F32, scale: Vector3, tint: Color) // Draw a model with extended parameters -DrawModelWires :: #foreign (model: Model, position: Vector3, scale: F32, tint: Color) // Draw a model wires (with texture if set) -DrawModelWiresEx :: #foreign (model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: F32, scale: Vector3, tint: Color) // Draw a model wires (with texture if set) with extended parameters -DrawBoundingBox :: #foreign (box: BoundingBox, color: Color) // Draw bounding box (wires) -DrawBillboard :: #foreign (camera: Camera, texture: Texture2D, position: Vector3, size: F32, tint: Color) // Draw a billboard texture -DrawBillboardRec :: #foreign (camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, size: Vector2, tint: Color) // Draw a billboard texture defined by source -DrawBillboardPro :: #foreign (camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, up: Vector3, size: Vector2, origin: Vector2, rotation: F32, tint: Color) // Draw a billboard texture defined by source and rotation - -// Mesh management functions - -UploadMesh :: #foreign (mesh: *Mesh, is_dynamic: bool) // Upload mesh vertex data in GPU and provide VAO/VBO ids -UpdateMeshBuffer :: #foreign (mesh: Mesh, index: int, data: *void, dataSize: int, offset: int) // Update mesh vertex data in GPU for a specific buffer index -UnloadMesh :: #foreign (mesh: Mesh) // Unload mesh data from CPU and GPU -DrawMesh :: #foreign (mesh: Mesh, material: Material, transform: Matrix) // Draw a 3d mesh with material and transform -DrawMeshInstanced :: #foreign (mesh: Mesh, material: Material, transforms: *Matrix, instances: int) // Draw multiple mesh instances with material and different transforms -ExportMesh :: #foreign (mesh: Mesh, fileName: *char): bool // Export mesh data to file, returns true on success -GetMeshBoundingBox :: #foreign (mesh: Mesh): BoundingBox // Compute mesh bounding box limits -GenMeshTangents :: #foreign (mesh: *Mesh) // Compute mesh tangents - -// Mesh generation functions - -GenMeshPoly :: #foreign (sides: int, radius: F32): Mesh // Generate polygonal mesh -GenMeshPlane :: #foreign (width: F32, lengthL: F32, resX: int, resZ: int): Mesh // Generate plane mesh (with subdivisions) -GenMeshCube :: #foreign (width: F32, height: F32, length: F32): Mesh // Generate cuboid mesh -GenMeshSphere :: #foreign (radius: F32, rings: int, slices: int): Mesh // Generate sphere mesh (standard sphere) -GenMeshHemiSphere :: #foreign (radius: F32, rings: int, slices: int): Mesh // Generate half-sphere mesh (no bottom cap) -GenMeshCylinder :: #foreign (radius: F32, height: F32, slices: int): Mesh // Generate cylinder mesh -GenMeshCone :: #foreign (radius: F32, height: F32, slices: int): Mesh // Generate cone/pyramid mesh -GenMeshTorus :: #foreign (radius: F32, size: F32, radSeg: int, sides: int): Mesh // Generate torus mesh -GenMeshKnot :: #foreign (radius: F32, size: F32, radSeg: int, sides: int): Mesh // Generate trefoil knot mesh -GenMeshHeightmap :: #foreign (heightmap: Image, size: Vector3): Mesh // Generate heightmap mesh from image data -GenMeshCubicmap :: #foreign (cubicmap: Image, cubeSize: Vector3): Mesh // Generate cubes-based map mesh from image data - -// Material loading/unloading functions - -LoadMaterials :: #foreign (fileName: *char, materialCount: *int): *Material // Load materials from model file -LoadMaterialDefault :: #foreign (): Material // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -IsMaterialReady :: #foreign (material: Material): bool // Check if a material is ready -UnloadMaterial :: #foreign (material: Material) // Unload material from GPU memory (VRAM) -SetMaterialTexture :: #foreign (material: *Material, mapType: MaterialMapIndex, texture: Texture2D) // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) -SetModelMeshMaterial :: #foreign (model: *Model, meshId: int, materialId: int) // Set material for a mesh - -// Model animations loading/unloading functions - -LoadModelAnimations :: #foreign (fileName: *char, animCount: *uint): *ModelAnimation // Load model animations from file -UpdateModelAnimation :: #foreign (model: Model, anim: ModelAnimation, frame: int) // Update model animation pose -UnloadModelAnimation :: #foreign (anim: ModelAnimation) // Unload animation data -UnloadModelAnimations :: #foreign (animations: *ModelAnimation, count: uint) // Unload animation array data -IsModelAnimationValid :: #foreign (model: Model, anim: ModelAnimation): bool // Check model animation skeleton match - -// Collision detection functions - -CheckCollisionSpheres :: #foreign (center1: Vector3, radius1: F32, center2: Vector3, radius2: F32): bool // Check collision between two spheres -CheckCollisionBoxes :: #foreign (box1: BoundingBox, box2: BoundingBox): bool // Check collision between two bounding boxes -CheckCollisionBoxSphere :: #foreign (box: BoundingBox, center: Vector3, radius: F32): bool // Check collision between box and sphere -GetRayCollisionSphere :: #foreign (ray: Ray, center: Vector3, radius: F32): RayCollision // Get collision info between ray and sphere -GetRayCollisionBox :: #foreign (ray: Ray, box: BoundingBox): RayCollision // Get collision info between ray and box -GetRayCollisionMesh :: #foreign (ray: Ray, mesh: Mesh, transform: Matrix): RayCollision // Get collision info between ray and mesh -GetRayCollisionTriangle :: #foreign (ray: Ray, p1: Vector3, p2: Vector3, p3: Vector3): RayCollision // Get collision info between ray and triangle -GetRayCollisionQuad :: #foreign (ray: Ray, p1: Vector4, p2: Vector4, p3: Vector4, p4: Vector3): RayCollision // Get collision info between ray and quad - -//------------------------------------------------------------------------------------ -// Audio Loading and Playing Functions (Module: audio) -//------------------------------------------------------------------------------------ - -// Audio device management functions - -InitAudioDevice :: #foreign () // Initialize audio device and context -CloseAudioDevice :: #foreign () // Close the audio device and context -IsAudioDeviceReady :: #foreign (): bool // Check if audio device has been initialized successfully -SetMasterVolume :: #foreign (volume: F32) // Set master volume (listener) - -// Wave/Sound loading/unloading functions - -LoadWave :: #foreign (fileName: *char): Wave // Load wave data from file -LoadWaveFromMemory :: #foreign (fileType: *char, fileData: *void, dataSize: int): Wave // Load wave from memory buffer, fileType refers to extension: i.e. '.wav' -IsWaveReady :: #foreign (wave: Wave): bool // Checks if wave data is ready -LoadSound :: #foreign (fileName: *char): Sound // Load sound from file -LoadSoundFromWave :: #foreign (wave: Wave): Sound // Load sound from wave data -IsSoundReady :: #foreign (sound: Sound): bool // Checks if a sound is ready -UpdateSound :: #foreign (sound: Sound, data: *void, sampleCount: int) // Update sound buffer with new data -UnloadWave :: #foreign (wave: Wave) // Unload wave data -UnloadSound :: #foreign (sound: Sound) // Unload sound -ExportWave :: #foreign (wave: Wave, fileName: *char): bool // Export wave data to file, returns true on success -ExportWaveAsCode :: #foreign (wave: Wave, fileName: *char): bool // Export wave sample data to code (.h), returns true on success - -// Wave/Sound management functions - -PlaySound :: #foreign (sound: Sound) // Play a sound -StopSound :: #foreign (sound: Sound) // Stop playing a sound -PauseSound :: #foreign (sound: Sound) // Pause a sound -ResumeSound :: #foreign (sound: Sound) // Resume a paused sound -IsSoundPlaying :: #foreign (sound: Sound): bool // Check if a sound is currently playing -SetSoundVolume :: #foreign (sound: Sound, volume: F32) // Set volume for a sound (1.0 is max level) -SetSoundPitch :: #foreign (sound: Sound, pitch: F32) // Set pitch for a sound (1.0 is base level) -SetSoundPan :: #foreign (sound: Sound, pan: F32) // Set pan for a sound (0.5 is center) -WaveCopy :: #foreign (wave: Wave): Wave // Copy a wave to a new wave -WaveCrop :: #foreign (wave: *Wave, initSample: int, finalSample: int) // Crop a wave to defined samples range -WaveFormat :: #foreign (wave: *Wave, sampleRate: int, sampleSize: int, channels: int) // Convert wave data to desired format -LoadWaveSamples :: #foreign (wave: Wave): *F32 // Load samples data from wave as a 32bit float data array -UnloadWaveSamples :: #foreign (samples: *F32) // Unload samples data loaded with LoadWaveSamples() - -// Music management functions - -LoadMusicStream :: #foreign (fileName: *char): Music // Load music stream from file -LoadMusicStreamFromMemory :: #foreign (fileType: *char, data: *void, dataSize: int): Music // Load music stream from data -IsMusicReady :: #foreign (music: Music): bool // Checks if a music stream is ready -UnloadMusicStream :: #foreign (music: Music) // Unload music stream -PlayMusicStream :: #foreign (music: Music) // Start music playing -IsMusicStreamPlaying :: #foreign (music: Music): bool // Check if music is playing -UpdateMusicStream :: #foreign (music: Music) // Updates buffers for music streaming -StopMusicStream :: #foreign (music: Music) // Stop music playing -PauseMusicStream :: #foreign (music: Music) // Pause music playing -ResumeMusicStream :: #foreign (music: Music) // Resume playing paused music -SeekMusicStream :: #foreign (music: Music, position: F32) // Seek music to a position (in seconds) -SetMusicVolume :: #foreign (music: Music, volume: F32) // Set volume for music (1.0 is max level) -SetMusicPitch :: #foreign (music: Music, pitch: F32) // Set pitch for a music (1.0 is base level) -SetMusicPan :: #foreign (music: Music, pan: F32) // Set pan for a music (0.5 is center) -GetMusicTimeLength :: #foreign (music: Music): F32 // Get music time length (in seconds) -GetMusicTimePlayed :: #foreign (music: Music): F32 // Get current music time played (in seconds) - -// AudioStream management functions - -LoadAudioStream :: #foreign (sampleRate: uint, sampleSize: uint, channels: uint): AudioStream // Load audio stream (to stream raw audio pcm data) -IsAudioStreamReady :: #foreign (stream: AudioStream): bool // Checks if an audio stream is ready -UnloadAudioStream :: #foreign (stream: AudioStream) // Unload audio stream and free memory -UpdateAudioStream :: #foreign (stream: AudioStream, data: *void, frameCount: int) // Update audio stream buffers with data -IsAudioStreamProcessed :: #foreign (stream: AudioStream): bool // Check if any audio stream buffers requires refill -PlayAudioStream :: #foreign (stream: AudioStream) // Play audio stream -PauseAudioStream :: #foreign (stream: AudioStream) // Pause audio stream -ResumeAudioStream :: #foreign (stream: AudioStream) // Resume audio stream -IsAudioStreamPlaying :: #foreign (stream: AudioStream): bool // Check if audio stream is playing -StopAudioStream :: #foreign (stream: AudioStream) // Stop audio stream -SetAudioStreamVolume :: #foreign (stream: AudioStream, volume: F32) // Set volume for audio stream (1.0 is max level) -SetAudioStreamPitch :: #foreign (stream: AudioStream, pitch: F32) // Set pitch for audio stream (1.0 is base level) -SetAudioStreamPan :: #foreign (stream: AudioStream, pan: F32) // Set pan for audio stream (0.5 is centered) -SetAudioStreamBufferSizeDefault :: #foreign (size: int) // Default size for new audio streams -SetAudioStreamCallback :: #foreign (stream: AudioStream, callback: AudioCallback) // Audio thread callback to request new data - -AttachAudioStreamProcessor :: #foreign (stream: AudioStream, processor: AudioCallback) // Attach audio stream processor to stream -DetachAudioStreamProcessor :: #foreign (stream: AudioStream, processor: AudioCallback) // Detach audio stream processor from stream - -AttachAudioMixedProcessor :: #foreign (processor: AudioCallback) // Attach audio stream processor to the entire audio pipeline -DetachAudioMixedProcessor :: #foreign (processor: AudioCallback) // Detach audio stream processor from the entire audio pipeline + +PI :: 3.14159265358979323846 +DEG2RAD :: PI/180.0 +RAD2DEG :: 180.0/PI + +Vector2 :: struct + x: F32 + y: F32 + +Vector3 :: struct + x: F32 + y: F32 + z: F32 + +Vector4 :: struct + x: F32 + y: F32 + z: F32 + w: F32 + +Color :: struct + r: uchar + g: uchar + b: uchar + a: uchar + +Rectangle :: struct + x: F32 + y: F32 + width: F32 + height: F32 + +Image :: struct + data: *void + width: int + height: int + mipmaps: int + format: int + +Texture :: struct + id: uint + width: int + height: int + mipmaps: int + format: int + +Texture2D :: Texture +TextureCubemap :: Texture + +RenderTexture :: struct + id: uint + texture: Texture + depth: Texture + +RenderTexture2D :: RenderTexture + +// N-Patch layout info +NPatchInfo :: struct + source: Rectangle // Texture source rectangle + left: int // Left border offset + top: int // Top border offset + right: int // Right border offset + bottom: int // Bottom border offset + layout: NPatchLayout // Layout of the n-patch: 3x3 1x3 or 3x1 + +// Font character info +GlyphInfo :: struct + value: rune // Character value (Unicode) + offsetX: int // Character offset X when drawing + offsetY: int // Character offset Y when drawing + advanceX: int // Character advance position X + image: Image // Character image data + +// Font type includes texture and charSet array data +Font :: struct + baseSize: int // Base size (default chars height) + charsCount: int // Number of characters + charsPadding: int // Padding around the chars + texture: Texture2D // Characters texture atlas + recs: *Rectangle // Characters rectangles in texture + chars: *GlyphInfo // Characters info data + +// Camera type defines a camera position/orientation in 3d space +Camera3D :: struct + position: Vector3 // Camera position + 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 + +Camera :: Camera3D // Camera type fallback defaults to Camera3D + +// Camera2D type defines a 2d camera +Camera2D :: struct + offset: Vector2 // Camera offset (displacement from target) + target: Vector2 // Camera target (rotation and zoom origin) + rotation: F32 // Camera rotation in degrees + zoom: F32 // Camera zoom (scaling) should be 1.0f by default + +// Vertex data definning a mesh +// NOTE: Data stored in CPU memory (and GPU) +Mesh :: struct + vertexCount: int // Number of vertices stored in arrays + triangleCount: int // Number of triangles stored (indexed or not) + + // Default vertex data + 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) + texcoords2: *F32 // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) + normals: *F32 // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) + tangents: *F32 // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) + colors: *U8 // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) + indices: *U16 // Vertex indices (in case vertex data comes indexed) + + // Animation vertex data + animVertices: *F32 // Animated vertex positions (after bones transformations) + animNormals: *F32 // Animated normals (after bones transformations) + boneIds: *U8 // Vertex bone ids up to 4 bones influence by vertex (skinning) + boneWeights: *F32 // Vertex bone weight up to 4 bones influence by vertex (skinning) + + // OpenGL identifiers + vaoId: U32 // OpenGL Vertex Array Object id + vboId: *U32 // OpenGL Vertex Buffer Objects id (default vertex data) + +// Shader type (generic) +Shader :: struct + id: uint // Shader program id + locs: *int // Shader locations array (MAX_SHADER_LOCATIONS) + +// Material texture map +MaterialMap :: struct + texture: Texture2D // Material map texture + color: Color // Material map color + value: F32 // Material map value + +// Material type (generic) +Material :: struct + shader: Shader // Material shader + maps: *MaterialMap // Material maps array (MAX_MATERIAL_MAPS) + params: [4]F32 // Material generic parameters (if required) + +// Transformation properties +Transform :: struct + translation: Vector3 // Translation + rotation: Quaternion // Rotation + scale: Vector3 // Scale + +// Bone information +BoneInfo :: struct + name: [32]char // Bone name + parent: int // Bone parent + +// Model type +Model :: struct + transform: Matrix // Local transform matrix + + meshCount: int // Number of meshes + materialCount: int // Number of materials + meshes: *Mesh // Meshes array + materials: *Material // Materials array + meshMaterial: *int // Mesh material number + + // Animation data + boneCount: int // Number of bones + bones: *BoneInfo // Bones information (skeleton) + bindPose: *Transform // Bones base transformation (pose) + +// Model animation +ModelAnimation :: struct + boneCount: int // Number of bones + frameCount: int // Number of animation frames + bones: *BoneInfo // Bones information (skeleton) + framePoses: **Transform // Poses array by frame + +// Ray type (useful for raycast) +Ray :: struct + position: Vector3 // Ray position (origin) + direction: Vector3 // Ray direction + +// RayCollision ray hit information +RayCollision :: struct + hit: bool // Did the ray hit something? + distance: F32 // Distance to nearest hit + point: Vector3 // Point of nearest hit + normal: Vector3 // Surface normal of hit + +// Bounding box type +BoundingBox :: struct + min: Vector3 // Minimum vertex box-corner + max: Vector3 // Maximum vertex box-corner + +// Wave type defines audio wave data +Wave :: struct + frameCount: uint // Total number of frames (considering channels) + sampleRate: uint // Frequency (samples per second) + sampleSize: uint // Bit depth (bits per sample): 8 16 32 (24 not supported) + channels: uint // Number of channels (1-mono 2-stereo) + data: *void // Buffer data pointer + +// Audio stream type +// NOTE: Actual structs are defined internally in raudio module +AudioStream :: struct + buffer: *void // Pointer to internal data used by the audio system + processor: *void // Pointer to internal data processor useful for audio effects + + sampleRate: uint // Frequency (samples per second) + sampleSize: uint // Bit depth (bits per sample): 8 16 32 (24 not supported) + channels: uint // Number of channels (1-mono 2-stereo) + +// Sound source type +Sound :: struct + stream: AudioStream // Audio stream + frameCount: uint // Total number of frames (considering channels) + +// Music stream type (audio file streaming from memory) +// NOTE: Anything longer than ~10 seconds should be streamed +Music :: struct + stream: AudioStream // Audio stream + frameCount: uint // Total number of frames (considering channels) + looping: bool // Music looping enable + + ctxType: int // Type of music context (audio filetype) + ctxData: *void // Audio context data depends on type + +// Head-Mounted-Display device parameters +VrDeviceInfo :: struct + hResolution: int // Horizontal resolution in pixels + vResolution: int // Vertical resolution in pixels + hScreenSize: F32 // Horizontal size in meters + vScreenSize: F32 // Vertical size in meters + vScreenCenter: F32 // Screen center in meters + eyeToScreenDistance: F32 // Distance between eye and display in meters + lensSeparationDistance: F32 // Lens separation distance in meters + interpupillaryDistance: F32 // IPD (distance between pupils) in meters + lensDistortionValues: [4]F32 // Lens distortion constant parameters + chromaAbCorrection: [4]F32 // Chromatic aberration correction parameters + +// VR Stereo rendering configuration for simulator +VrStereoConfig :: struct + projection: [2]Matrix // VR projection matrices (per eye) + viewOffset: [2]Matrix // VR view offset matrices (per eye) + leftLensCenter: [2]F32 // VR left lens center + rightLensCenter: [2]F32 // VR right lens center + leftScreenCenter: [2]F32 // VR left screen center + rightScreenCenter: [2]F32 // VR right screen center + scale: [2]F32 // VR distortion scale + scaleIn: [2]F32 // VR distortion scale in + +// File path list +FilePathList :: struct + capacity: uint // Filepaths max entries + count: uint // Filepaths entries count + paths: **char // Filepaths entries + +// 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 +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) + +FLAG_VSYNC_HINT :: 0x00000040 // Set to try enabling V-Sync on GPU +FLAG_FULLSCREEN_MODE :: 0x00000002 // Set to run program in fullscreen +FLAG_WINDOW_RESIZABLE :: 0x00000004 // Set to allow resizable window +FLAG_WINDOW_UNDECORATED :: 0x00000008 // Set to disable window decoration (frame and buttons) +FLAG_WINDOW_HIDDEN :: 0x00000080 // Set to hide window +FLAG_WINDOW_MINIMIZED :: 0x00000200 // Set to minimize window (iconify) +FLAG_WINDOW_MAXIMIZED :: 0x00000400 // Set to maximize window (expanded to monitor) +FLAG_WINDOW_UNFOCUSED :: 0x00000800 // Set to window non focused +FLAG_WINDOW_TOPMOST :: 0x00001000 // Set to window always on top +FLAG_WINDOW_ALWAYS_RUN :: 0x00000100 // Set to allow windows running while minimized +FLAG_WINDOW_TRANSPARENT :: 0x00000010 // Set to allow transparent framebuffer +FLAG_WINDOW_HIGHDPI :: 0x00002000 // Set to support HighDPI +FLAG_WINDOW_MOUSE_PASSTHROUGH :: 0x00004000 // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED +FLAG_MSAA_4X_HINT :: 0x00000020 // Set to try enabling MSAA 4X +FLAG_INTERLACED_HINT :: 0x00010000 // Set to try enabling interlaced video format (for V3D) + +LOG_ALL :: 0 // Display all logs +LOG_TRACE :: 1 // Trace logging, intended for internal use only +LOG_DEBUG :: 2 // Debug logging, used for internal debugging, it should be disabled on release builds +LOG_INFO :: 3 // Info logging, used for program execution info +LOG_WARNING :: 4 // Warning logging, used on recoverable failures +LOG_ERROR :: 5 // Error logging, used on unrecoverable failures +LOG_FATAL :: 6 // Fatal logging, used to abort program: exit(EXIT_FAILURE) +LOG_NONE :: 7 // Disable logging + +KEY_NULL :: 0 // Key: NULL, used for no key pressed +// Alphanumeric keys +KEY_APOSTROPHE :: 39 // Key: ' +KEY_COMMA :: 44 // Key: , +KEY_MINUS :: 45 // Key: - +KEY_PERIOD :: 46 // Key: . +KEY_SLASH :: 47 // Key: / +KEY_ZERO :: 48 // Key: 0 +KEY_ONE :: 49 // Key: 1 +KEY_TWO :: 50 // Key: 2 +KEY_THREE :: 51 // Key: 3 +KEY_FOUR :: 52 // Key: 4 +KEY_FIVE :: 53 // Key: 5 +KEY_SIX :: 54 // Key: 6 +KEY_SEVEN :: 55 // Key: 7 +KEY_EIGHT :: 56 // Key: 8 +KEY_NINE :: 57 // Key: 9 +KEY_SEMICOLON :: 59 // Key: ; +KEY_EQUAL :: 61 // Key: :: +KEY_A :: 65 // Key: A | a +KEY_B :: 66 // Key: B | b +KEY_C :: 67 // Key: C | c +KEY_D :: 68 // Key: D | d +KEY_E :: 69 // Key: E | e +KEY_F :: 70 // Key: F | f +KEY_G :: 71 // Key: G | g +KEY_H :: 72 // Key: H | h +KEY_I :: 73 // Key: I | i +KEY_J :: 74 // Key: J | j +KEY_K :: 75 // Key: K | k +KEY_L :: 76 // Key: L | l +KEY_M :: 77 // Key: M | m +KEY_N :: 78 // Key: N | n +KEY_O :: 79 // Key: O | o +KEY_P :: 80 // Key: P | p +KEY_Q :: 81 // Key: Q | q +KEY_R :: 82 // Key: R | r +KEY_S :: 83 // Key: S | s +KEY_T :: 84 // Key: T | t +KEY_U :: 85 // Key: U | u +KEY_V :: 86 // Key: V | v +KEY_W :: 87 // Key: W | w +KEY_X :: 88 // Key: X | x +KEY_Y :: 89 // Key: Y | y +KEY_Z :: 90 // Key: Z | z +KEY_LEFT_BRACKET :: 91 // Key: [ +KEY_BACKSLASH :: 92 // Key: '\' +KEY_RIGHT_BRACKET :: 93 // Key: ] +KEY_GRAVE :: 96 // Key: ` +// Function keys +KEY_SPACE :: 32 // Key: Space +KEY_ESCAPE :: 256 // Key: Esc +KEY_ENTER :: 257 // Key: Enter +KEY_TAB :: 258 // Key: Tab +KEY_BACKSPACE :: 259 // Key: Backspace +KEY_INSERT :: 260 // Key: Ins +KEY_DELETE :: 261 // Key: Del +KEY_RIGHT :: 262 // Key: Cursor right +KEY_LEFT :: 263 // Key: Cursor left +KEY_DOWN :: 264 // Key: Cursor down +KEY_UP :: 265 // Key: Cursor up +KEY_PAGE_UP :: 266 // Key: Page up +KEY_PAGE_DOWN :: 267 // Key: Page down +KEY_HOME :: 268 // Key: Home +KEY_END :: 269 // Key: End +KEY_CAPS_LOCK :: 280 // Key: Caps lock +KEY_SCROLL_LOCK :: 281 // Key: Scroll down +KEY_NUM_LOCK :: 282 // Key: Num lock +KEY_PRINT_SCREEN :: 283 // Key: Print screen +KEY_PAUSE :: 284 // Key: Pause +KEY_F1 :: 290 // Key: F1 +KEY_F2 :: 291 // Key: F2 +KEY_F3 :: 292 // Key: F3 +KEY_F4 :: 293 // Key: F4 +KEY_F5 :: 294 // Key: F5 +KEY_F6 :: 295 // Key: F6 +KEY_F7 :: 296 // Key: F7 +KEY_F8 :: 297 // Key: F8 +KEY_F9 :: 298 // Key: F9 +KEY_F10 :: 299 // Key: F10 +KEY_F11 :: 300 // Key: F11 +KEY_F12 :: 301 // Key: F12 +KEY_LEFT_SHIFT :: 340 // Key: Shift left +KEY_LEFT_CONTROL :: 341 // Key: Control left +KEY_LEFT_ALT :: 342 // Key: Alt left +KEY_LEFT_SUPER :: 343 // Key: Super left +KEY_RIGHT_SHIFT :: 344 // Key: Shift right +KEY_RIGHT_CONTROL :: 345 // Key: Control right +KEY_RIGHT_ALT :: 346 // Key: Alt right +KEY_RIGHT_SUPER :: 347 // Key: Super right +KEY_KB_MENU :: 348 // Key: KB menu +// Keypad keys +KEY_KP_0 :: 320 // Key: Keypad 0 +KEY_KP_1 :: 321 // Key: Keypad 1 +KEY_KP_2 :: 322 // Key: Keypad 2 +KEY_KP_3 :: 323 // Key: Keypad 3 +KEY_KP_4 :: 324 // Key: Keypad 4 +KEY_KP_5 :: 325 // Key: Keypad 5 +KEY_KP_6 :: 326 // Key: Keypad 6 +KEY_KP_7 :: 327 // Key: Keypad 7 +KEY_KP_8 :: 328 // Key: Keypad 8 +KEY_KP_9 :: 329 // Key: Keypad 9 +KEY_KP_DECIMAL :: 330 // Key: Keypad . +KEY_KP_DIVIDE :: 331 // Key: Keypad / +KEY_KP_MULTIPLY :: 332 // Key: Keypad * +KEY_KP_SUBTRACT :: 333 // Key: Keypad - +KEY_KP_ADD :: 334 // Key: Keypad + +KEY_KP_ENTER :: 335 // Key: Keypad Enter +KEY_KP_EQUAL :: 336 // Key: Keypad :: +// Android key buttons +KEY_BACK :: 4 // Key: Android back button +KEY_MENU :: 82 // Key: Android menu button +KEY_VOLUME_UP :: 24 // Key: Android volume up button +KEY_VOLUME_DOWN :: 25 // Key: Android volume down button + + +MOUSE_BUTTON_LEFT :: 0 // Mouse button left +MOUSE_BUTTON_RIGHT :: 1 // Mouse button right +MOUSE_BUTTON_MIDDLE :: 2 // Mouse button middle (pressed wheel) +MOUSE_BUTTON_SIDE :: 3 // Mouse button side (advanced mouse device) +MOUSE_BUTTON_EXTRA :: 4 // Mouse button extra (advanced mouse device) +MOUSE_BUTTON_FORWARD :: 5 // Mouse button forward (advanced mouse device) +MOUSE_BUTTON_BACK :: 6 // Mouse button back (advanced mouse device) + + +InitWindow :: #foreign (width: int, height: int, title: *char) // Initialize window and OpenGL context +WindowShouldClose :: #foreign (): bool // Check if KEY_ESCAPE pressed or Close icon pressed +CloseWindow :: #foreign () // Close window and unload OpenGL context +IsWindowReady :: #foreign (): bool // Check if window has been initialized successfully +IsWindowFullscreen :: #foreign (): bool // Check if window is currently fullscreen +IsWindowHidden :: #foreign (): bool // Check if window is currently hidden (only PLATFORM_DESKTOP) +IsWindowMinimized :: #foreign (): bool // Check if window is currently minimized (only PLATFORM_DESKTOP) +IsWindowMaximized :: #foreign (): bool // Check if window is currently maximized (only PLATFORM_DESKTOP) +IsWindowFocused :: #foreign (): bool // Check if window is currently focused (only PLATFORM_DESKTOP) +IsWindowResized :: #foreign (): bool // Check if window has been resized last frame +IsWindowState :: #foreign (flag: ConfigFlag): bool // Check if one specific window flag is enabled +SetWindowState :: #foreign (flags: ConfigFlags) // Set window configuration state using flags (only PLATFORM_DESKTOP) +ClearWindowState :: #foreign (flags: ConfigFlags) // Clear window configuration state flags +ToggleFullscreen :: #foreign () // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) +MaximizeWindow :: #foreign () // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) +MinimizeWindow :: #foreign () // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) +RestoreWindow :: #foreign () // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) +SetWindowIcon :: #foreign (image: Image) // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) +SetWindowIcons :: #foreign (images: *Image, count: int) // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) +SetWindowTitle :: #foreign (title: *char) // Set title for window (only PLATFORM_DESKTOP) +SetWindowPosition :: #foreign (x: int, y: int) // Set window position on screen (only PLATFORM_DESKTOP) +SetWindowMonitor :: #foreign (monitor: int) // Set monitor for the current window (fullscreen mode) +SetWindowMinSize :: #foreign (width: int, height: int) // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) +SetWindowSize :: #foreign (width: int, height: int) // Set window dimensions +SetWindowOpacity :: #foreign (opacity: F32) // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) +GetWindowHandle :: #foreign (): *void // Get native window handle +GetScreenWidth :: #foreign (): int // Get current screen width +GetScreenHeight :: #foreign (): int // Get current screen height +GetRenderWidth :: #foreign (): int // Get current render width (it considers HiDPI) +GetRenderHeight :: #foreign (): int // Get current render height (it considers HiDPI) +GetMonitorCount :: #foreign (): int // Get number of connected monitors +GetCurrentMonitor :: #foreign (): int // Get current connected monitor +GetMonitorPosition :: #foreign (monitor: int): Vector2 // Get specified monitor position +GetMonitorWidth :: #foreign (monitor: int): int // Get specified monitor width (current video mode used by monitor) +GetMonitorHeight :: #foreign (monitor: int): int // Get specified monitor height (current video mode used by monitor) +GetMonitorPhysicalWidth :: #foreign (monitor: int): int // Get specified monitor physical width in millimetres +GetMonitorPhysicalHeight :: #foreign (monitor: int): int // Get specified monitor physical height in millimetres +GetMonitorRefreshRate :: #foreign (monitor: int): int // Get specified monitor refresh rate +GetWindowPosition :: #foreign (): Vector2 // Get window position XY on monitor +GetWindowScaleDPI :: #foreign (): Vector2 // Get window scale DPI factor +GetMonitorName :: #foreign (monitor: int): *char // Get the human-readable, UTF-8 encoded name of the primary monitor +SetClipboardText :: #foreign (text: *char) // Set clipboard text content +GetClipboardText :: #foreign (): *char // Get clipboard text content +EnableEventWaiting :: #foreign () // Enable waiting for events on EndDrawing(), no automatic event polling +DisableEventWaiting :: #foreign () // Disable waiting for events on EndDrawing(), automatic events polling + + +// Custom frame control functions +// NOTE: Those functions are intended for advance users that want full control over the frame processing +// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents() +// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL + +SwapScreenBuffer :: #foreign () // Swap back buffer with front buffer (screen drawing) +PollInputEvents :: #foreign () // Register all input events +WaitTime :: #foreign (seconds: F64) // Wait for some time (halt program execution) + + +// Cursor-related functions + +ShowCursor :: #foreign () // Shows cursor +HideCursor :: #foreign () // Hides cursor +IsCursorHidden :: #foreign (): bool // Check if cursor is not visible +EnableCursor :: #foreign () // Enables cursor (unlock cursor) +DisableCursor :: #foreign () // Disables cursor (lock cursor) +IsCursorOnScreen :: #foreign (): bool // Check if cursor is on the current screen. + +// Drawing-related functions + +ClearBackground :: #foreign (color: Color) // Set background color (framebuffer clear color) +BeginDrawing :: #foreign () // Setup canvas (framebuffer) to start drawing +EndDrawing :: #foreign () // End canvas drawing and swap buffers (double buffering) +BeginMode2D :: #foreign (camera: Camera2D) // Initialize 2D mode with custom camera (2D) +EndMode2D :: #foreign () // Ends 2D mode with custom camera +BeginMode3D :: #foreign (camera: Camera3D) // Initializes 3D mode with custom camera (3D) +EndMode3D :: #foreign () // Ends 3D mode and returns to default 2D orthographic mode +BeginTextureMode :: #foreign (target: RenderTexture2D) // Initializes render texture for drawing +EndTextureMode :: #foreign () // Ends drawing to render texture +BeginShaderMode :: #foreign (shader: Shader) // Begin custom shader drawing +EndShaderMode :: #foreign () // End custom shader drawing (use default shader) +BeginBlendMode :: #foreign (mode: BlendMode) // Begin blending mode (alpha, additive, multiplied) +EndBlendMode :: #foreign () // End blending mode (reset to default: alpha blending) +BeginScissorMode :: #foreign (x: int, y: int, width: int, height: int) // Begin scissor mode (define screen area for following drawing) +EndScissorMode :: #foreign () // End scissor mode +BeginVrStereoMode :: #foreign (config: VrStereoConfig) // Begin stereo rendering (requires VR simulator) +EndVrStereoMode :: #foreign () // End stereo rendering (requires VR simulator) + +// VR stereo config functions for VR simulator + +// LoadVrStereoConfig :: #foreign (device: VrDeviceInfo): VrStereoConfig // Load VR stereo config for VR simulator device parameters +// UnloadVrStereoConfig :: #foreign (config: VrStereoConfig) // Unload VR stereo config + +// Shader management functions +// NOTE: Shader functionality is not available on OpenGL 1.1 + +LoadShader :: #foreign (vsFileName: *char, fsFileName: *char): Shader // Load shader from files and bind default locations +LoadShaderFromMemory :: #foreign (vsCode: *char, fsCode: *char): Shader // Load shader from code strings and bind default locations +IsShaderReady :: #foreign (shader: Shader): bool // Check if a shader is ready +GetShaderLocation :: #foreign (shader: Shader, uniformName: *char): int // Get shader uniform location +GetShaderLocationAttrib :: #foreign (shader: Shader, attribName: *char): int // Get shader attribute location +SetShaderValue :: #foreign (shader: Shader, locIndex: ShaderLocationIndex, value: *void, uniformType: ShaderUniformDataType) // Set shader uniform value +SetShaderValueV :: #foreign (shader: Shader, locIndex: ShaderLocationIndex, value: *void, uniformType: ShaderUniformDataType, count: int) // Set shader uniform value vector +SetShaderValueMatrix :: #foreign (shader: Shader, locIndex: ShaderLocationIndex, mat: Matrix) // Set shader uniform value (matrix 4x4) +SetShaderValueTexture :: #foreign (shader: Shader, locIndex: ShaderLocationIndex, texture: Texture2D) // Set shader uniform value for texture (sampler2d) +UnloadShader :: #foreign (shader: Shader) // Unload shader from GPU memory (VRAM) + +// Screen-space-related functions + +GetMouseRay :: #foreign (mousePosition: Vector2, camera: Camera): Ray // Get a ray trace from mouse position +GetCameraMatrix :: #foreign (camera: Camera): Matrix // Get camera transform matrix (view matrix) +GetCameraMatrix2D :: #foreign (camera: Camera2D): Matrix // Get camera 2d transform matrix +GetWorldToScreen :: #foreign (position: Vector3, camera: Camera): Vector2 // Get the screen space position for a 3d world space position +GetScreenToWorld2D :: #foreign (position: Vector2, camera: Camera2D): Vector2 // Get the world space position for a 2d camera screen space position +GetWorldToScreenEx :: #foreign (position: Vector3, camera: Camera, width: int, height: int): Vector2 // Get size position for a 3d world space position +GetWorldToScreen2D :: #foreign (position: Vector2, camera: Camera2D): Vector2 // Get the screen space position for a 2d camera world space position + +// Timing-related functions + +SetTargetFPS :: #foreign (fps: int) // Set target FPS (maximum) +GetFPS :: #foreign (): int // Returns current FPS +GetFrameTime :: #foreign (): F32 // Returns time in seconds for last frame drawn (delta time) +GetTime :: #foreign (): F64 // Returns elapsed time in seconds since InitWindow() + +// Misc. functions + +GetRandomValue :: #foreign (min: int, max: int): int // Returns a random value between min and max (both included) +SetRandomSeed :: #foreign (seed: uint) // Set the seed for the random number generator +TakeScreenshot :: #foreign (fileName: *char) // Takes a screenshot of current screen (filename extension defines format) +SetConfigFlags :: #foreign (flags: ConfigFlags) // Setup init configuration flags (view FLAGS) + +TraceLog :: #foreign (logLevel: TraceLogLevel, text: *char, ...) // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR) +SetTraceLogLevel :: #foreign (logLevel: TraceLogLevel) // Set the current threshold (minimum) log level +MemAlloc :: #foreign (size: uint): *void // Internal memory allocator +MemRealloc :: #foreign (ptr: *void, size: uint): *void // Internal memory reallocator +MemFree :: #foreign (ptr: *void) // Internal memory free + +OpenURL :: #foreign (url: *char) // Open URL with default system browser (if available) + +// Set custom callbacks +// WARNING: Callbacks setup is intended for advance users + +SetTraceLogCallback :: #foreign (callback: TraceLogCallback) // Set custom trace log +SetLoadFileDataCallback :: #foreign (callback: LoadFileDataCallback) // Set custom file binary data loader +SetSaveFileDataCallback :: #foreign (callback: SaveFileDataCallback) // Set custom file binary data saver +SetLoadFileTextCallback :: #foreign (callback: LoadFileTextCallback) // Set custom file text data loader +SetSaveFileTextCallback :: #foreign (callback: SaveFileTextCallback) // Set custom file text data saver + +// Files management functions + +LoadFileData :: #foreign (fileName: *char, bytesRead: *uint): *uchar // Load file data as byte array (read) +UnloadFileData :: #foreign (data: *uchar) // Unload file data allocated by LoadFileData() +SaveFileData :: #foreign (fileName: *char, data: *void, bytesToWrite: uint): bool // Save data to file from byte array (write), returns true on success +ExportDataAsCode :: #foreign (data: *void, size: uint, fileName: *char): bool // Export data to code (.h), returns true on success +LoadFileText :: #foreign (fileName: *char): *uchar // Load text data from file (read), returns a '\0' terminated string +UnloadFileText :: #foreign (text: *uchar) // Unload file text data allocated by LoadFileText() +SaveFileText :: #foreign (fileName: *char, text: *uchar): bool // Save text data to file (write), string must be '\0' terminated, returns true on success +FileExists :: #foreign (fileName: *char): bool // Check if file exists +DirectoryExists :: #foreign (dirPath: *char): bool // Check if a directory path exists +IsFileExtension :: #foreign (fileName: *char, ext: *char): bool // Check file extension (including point: .png, .wav) +GetFileLength :: #foreign (fileName: *char): int // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) +GetFileExtension :: #foreign (fileName: *char): *char // Get pointer to extension for a filename string (includes dot: '.png') +GetFileName :: #foreign (filePath: *char): *char // Get pointer to filename for a path string +GetFileNameWithoutExt :: #foreign (filePath: *char): *char // Get filename string without extension (uses static string) +GetDirectoryPath :: #foreign (filePath: *char): *char // Get full path for a given fileName with path (uses static string) +GetPrevDirectoryPath :: #foreign (dirPath: *char): *char // Get previous directory path for a given path (uses static string) +GetWorkingDirectory :: #foreign (): *char // Get current working directory (uses static string) +GetApplicationDirectory :: #foreign (): *char // Get the directory if the running application (uses static string) +ChangeDirectory :: #foreign (dir: *char): bool // Change working directory, return true on success +IsPathFile :: #foreign (path: *char): bool // Check if a given path is a file or a directory +LoadDirectoryFiles :: #foreign (dirPath: *char): FilePathList // Load directory filepaths +LoadDirectoryFilesEx :: #foreign (basePath: *char, filter: *char, scanSubdirs: bool): FilePathList // Load directory filepaths with extension filtering and recursive directory scan +UnloadDirectoryFiles :: #foreign (files: FilePathList) // Unload filepaths +IsFileDropped :: #foreign (): bool // Check if a file has been dropped into window +LoadDroppedFiles :: #foreign (): FilePathList // Load dropped filepaths +UnloadDroppedFiles :: #foreign (files: FilePathList) // Unload dropped filepaths +GetFileModTime :: #foreign (fileName: *char): long // Get file modification time (last write time) + +// Compression/Encoding functionality + +CompressData :: #foreign (data: *void, dataSize: int, compDataSize: *int): *uchar // Compress data (DEFLATE algorithm), memory must be MemFree() +DecompressData :: #foreign (compData: *void, compDataSize: int, dataSize: *int): *uchar // Decompress data (DEFLATE algorithm), memory must be MemFree() +EncodeDataBase64 :: #foreign (data: *void, dataSize: int, outputSize: *int): *uchar // Encode data to Base64 string, memory must be MemFree() +DecodeDataBase64 :: #foreign (data: *void, outputSize: *int): *uchar // Decode Base64 string data, memory must be MemFree() + +//------------------------------------------------------------------------------------ +// Input Handling Functions (Module: core) +//------------------------------------------------------------------------------------ + +// Input-related functions: keyboard + +IsKeyPressed :: #foreign (key: int): bool // Detect if a key has been pressed once +IsKeyDown :: #foreign (key: int): bool // Detect if a key is being pressed +IsKeyReleased :: #foreign (key: int): bool // Detect if a key has been released once +IsKeyUp :: #foreign (key: int): bool // Detect if a key is NOT being pressed +SetExitKey :: #foreign (key: int) // Set a custom key to exit program (default is ESC) +GetKeyPressed :: #foreign (): int // Get key pressed (keycode), call it multiple times for keys queued +GetCharPressed :: #foreign (): rune // Get char pressed (unicode), call it multiple times for chars queued + +// Input-related functions: gamepads + +IsGamepadAvailable :: #foreign (gamepad: int): bool // Check if a gamepad is available +GetGamepadName :: #foreign (gamepad: int): *char // Get gamepad internal name id +IsGamepadButtonPressed :: #foreign (gamepad: int, button: GamepadButton): bool // Check if a gamepad button has been pressed once +IsGamepadButtonDown :: #foreign (gamepad: int, button: GamepadButton): bool // Check if a gamepad button is being pressed +IsGamepadButtonReleased :: #foreign (gamepad: int, button: GamepadButton): bool // Check if a gamepad button has been released once +IsGamepadButtonUp :: #foreign (gamepad: int, button: GamepadButton): bool // Check if a gamepad button is NOT being pressed +GetGamepadButtonPressed :: #foreign (): GamepadButton // Get the last gamepad button pressed +GetGamepadAxisCount :: #foreign (gamepad: int): int // Get gamepad axis count for a gamepad +GetGamepadAxisMovement :: #foreign (gamepad: int, axis: GamepadAxis): F32 // Get axis movement value for a gamepad axis +SetGamepadMappings :: #foreign (mappings: *char): int // Set internal gamepad mappings (SDL_GameControllerDB) + +// Input-related functions: mouse + +IsMouseButtonPressed :: #foreign (button: int): bool // Detect if a mouse button has been pressed once +IsMouseButtonDown :: #foreign (button: int): bool // Detect if a mouse button is being pressed +IsMouseButtonReleased :: #foreign (button: int): bool // Detect if a mouse button has been released once +IsMouseButtonUp :: #foreign (button: int): bool // Detect if a mouse button is NOT being pressed +GetMouseX :: #foreign (): int // Returns mouse position X +GetMouseY :: #foreign (): int // Returns mouse position Y +GetMousePosition :: #foreign (): Vector2 // Returns mouse position XY +GetMouseDelta :: #foreign (): Vector2 // Returns mouse delta XY +SetMousePosition :: #foreign (x: int, y: int) // Set mouse position XY +SetMouseOffset :: #foreign (offsetX: int, offsetY: int) // Set mouse offset +SetMouseScale :: #foreign (scaleX: F32, scaleY: F32) // Set mouse scaling +GetMouseWheelMove :: #foreign (): F32 // Returns mouse wheel movement Y +GetMouseWheelMoveV :: #foreign (): Vector2 // Get mouse wheel movement for both X and Y +SetMouseCursor :: #foreign (cursor: MouseCursor) // Set mouse cursor + +// Input-related functions: touch + +GetTouchX :: #foreign (): int // Returns touch position X for touch point 0 (relative to screen size) +GetTouchY :: #foreign (): int // Returns touch position Y for touch point 0 (relative to screen size) +GetTouchPosition :: #foreign (index: int): Vector2 // Returns touch position XY for a touch point index (relative to screen size) +GetTouchPointId :: #foreign (index: int): int // Get touch point identifier for given index +GetTouchPointCount :: #foreign (): int // Get number of touch points + +//------------------------------------------------------------------------------------ +// Gestures and Touch Handling Functions (Module: rgestures) +//------------------------------------------------------------------------------------ + +SetGesturesEnabled :: #foreign (flags: Gestures) // Enable a set of gestures using flags +IsGestureDetected :: #foreign (gesture: Gesture): bool // Check if a gesture have been detected +GetGestureDetected :: #foreign (): Gesture // Get latest detected gesture +GetGestureHoldDuration :: #foreign (): F32 // Get gesture hold time in milliseconds +GetGestureDragVector :: #foreign (): Vector2 // Get gesture drag vector +GetGestureDragAngle :: #foreign (): F32 // Get gesture drag angle +GetGesturePinchVector :: #foreign (): Vector2 // Get gesture pinch delta +GetGesturePinchAngle :: #foreign (): F32 // Get gesture pinch angle + +//------------------------------------------------------------------------------------ +// Camera System Functions (Module: camera) +//------------------------------------------------------------------------------------ + +UpdateCamera :: #foreign (camera: *Camera, mode: CameraMode) // Set camera mode (multiple camera modes available) +UpdateCameraPro :: #foreign (camera: *Camera, movement: Vector3, rotation: Vector3, zoom: F32) // Update camera movement/rotation + +//------------------------------------------------------------------------------------ +// Basic Shapes Drawing Functions (Module: shapes) +//------------------------------------------------------------------------------------ +// Set texture and rectangle to be used on shapes drawing +// NOTE: It can be useful when using basic shapes and one single font, +// defining a font char white rectangle would allow drawing everything in a single draw call + +SetShapesTexture :: #foreign (texture: Texture2D, source: Rectangle) + +// Basic shapes drawing functions + +DrawPixel :: #foreign (posX: int, posY: int, color: Color) // Draw a pixel +DrawPixelV :: #foreign (position: Vector2, color: Color) // Draw a pixel (Vector version) +DrawLine :: #foreign (startPosX: int, startPosY: int, endPosX: int, endPosY: int, color: Color) // Draw a line +DrawLineV :: #foreign (startPos: Vector2, endPos: Vector2, color: Color) // Draw a line (Vector version) +DrawLineEx :: #foreign (startPos: Vector2, endPos: Vector2, thick: F32, color: Color) // Draw a line defining thickness +DrawLineBezier :: #foreign (startPos: Vector2, endPos: Vector2, thick: F32, color: Color) // Draw a line using cubic-bezier curves in-out +DrawLineBezierQuad :: #foreign (startPos: Vector2, endPos: Vector2, controlPos: Vector2, thick: F32, color: Color) // Draw line using quadratic bezier curves with a control point +DrawLineBezierCubic :: #foreign (startPos: Vector2, endPos: Vector2, startControlPos: Vector2, endControlPos: Vector2, thick: F32, color: Color) // Draw line using cubic bezier curves with 2 control points +DrawLineStrip :: #foreign (points: *Vector2, pointCount: int, color: Color) // Draw lines sequence +DrawCircle :: #foreign (centerX: int, centerY: int, radius: F32, color: Color) // Draw a color-filled circle +DrawCircleSector :: #foreign (center: Vector2, radius: F32, startAngle: F32, endAngle: F32, segments: int, color: Color) // Draw a piece of a circle +DrawCircleSectorLines :: #foreign (center: Vector2, radius: F32, startAngle: F32, endAngle: F32, segments: int, color: Color) // Draw circle sector outline +DrawCircleGradient :: #foreign (centerX: int, centerY: int, radius: F32, color1: Color, color2: Color) // Draw a gradient-filled circle +DrawCircleV :: #foreign (center: Vector2, radius: F32, color: Color) // Draw a color-filled circle (Vector version) +DrawCircleLines :: #foreign (centerX: int, centerY: int, radius: F32, color: Color) // Draw circle outline +DrawEllipse :: #foreign (centerX: int, centerY: int, radiusH: F32, radiusV: F32, color: Color) // Draw ellipse +DrawEllipseLines :: #foreign (centerX: int, centerY: int, radiusH: F32, radiusV: F32, color: Color) // Draw ellipse outline +DrawRing :: #foreign (center: Vector2, innerRadius: F32, outerRadius: F32, startAngle: F32, endAngle: F32, segments: int, color: Color) // Draw ring +DrawRingLines :: #foreign (center: Vector2, innerRadius: F32, outerRadius: F32, startAngle: F32, endAngle: F32, segments: int, color: Color) // Draw ring outline +DrawRectangle :: #foreign (posX: int, posY: int, width: int, height: int, color: Color) // Draw a color-filled rectangle +DrawRectangleV :: #foreign (position: Vector2, size: Vector2, color: Color) // Draw a color-filled rectangle (Vector version) +DrawRectangleRec :: #foreign (rec: Rectangle, color: Color) // Draw a color-filled rectangle +DrawRectanglePro :: #foreign (rec: Rectangle, origin: Vector2, rotation: F32, color: Color) // Draw a color-filled rectangle with pro parameters +DrawRectangleGradientV :: #foreign (posX: int, posY: int, width: int, height: int, color1: Color, color2: Color) // Draw a vertical-gradient-filled rectangle +DrawRectangleGradientH :: #foreign (posX: int, posY: int, width: int, height: int, color1: Color, color2: Color) // Draw a horizontal-gradient-filled rectangle +DrawRectangleGradientEx :: #foreign (rec: Rectangle, col1: Color, col2: Color, col3: Color, col4: Color) // Draw a gradient-filled rectangle with custom vertex colors +DrawRectangleLines :: #foreign (posX: int, posY: int, width: int, height: int, color: Color) // Draw rectangle outline +DrawRectangleLinesEx :: #foreign (rec: Rectangle, lineThick: F32, color: Color) // Draw rectangle outline with extended parameters +DrawRectangleRounded :: #foreign (rec: Rectangle, roundness: F32, segments: int, color: Color) // Draw rectangle with rounded edges +DrawRectangleRoundedLines :: #foreign (rec: Rectangle, roundness: F32, segments: int, lineThick: F32, color: Color) // Draw rectangle with rounded edges outline +DrawTriangle :: #foreign (v1: Vector2, v2: Vector2, v3: Vector2, color: Color) // Draw a color-filled triangle (vertex in counter-clockwise order!) +DrawTriangleLines :: #foreign (v1: Vector2, v2: Vector2, v3: Vector2, color: Color) // Draw triangle outline (vertex in counter-clockwise order!) +DrawTriangleFan :: #foreign (points: *Vector2, pointCount: int, color: Color) // Draw a triangle fan defined by points (first vertex is the center) +DrawTriangleStrip :: #foreign (points: *Vector2, pointCount: int, color: Color) // Draw a triangle strip defined by points +DrawPoly :: #foreign (center: Vector2, sides: int, radius: F32, rotation: F32, color: Color) // Draw a regular polygon (Vector version) +DrawPolyLines :: #foreign (center: Vector2, sides: int, radius: F32, rotation: F32, color: Color) // Draw a polygon outline of n sides +DrawPolyLinesEx :: #foreign (center: Vector2, sides: int, radius: F32, rotation: F32, lineThick: F32, color: Color) // Draw a polygon outline of n sides with extended parameters + + // Basic shapes collision detection functions +CheckCollisionRecs :: #foreign (rec1: Rectangle, rec2: Rectangle): bool // Check collision between two rectangles +CheckCollisionCircles :: #foreign (center1: Vector2, radius1: F32, center2: Vector2, radius2: F32): bool // Check collision between two circles +CheckCollisionCircleRec :: #foreign (center: Vector2, radius: F32, rec: Rectangle): bool // Check collision between circle and rectangle +CheckCollisionPointRec :: #foreign (point: Vector2, rec: Rectangle): bool // Check if point is inside rectangle +CheckCollisionPointCircle :: #foreign (point: Vector2, center: Vector2, radius: F32): bool // Check if point is inside circle +CheckCollisionPointTriangle :: #foreign (point: Vector2, p1: Vector2, p2: Vector2, p3: Vector2): bool // Check if point is inside a triangle +CheckCollisionPointPoly :: #foreign (point: Vector2, points: *Vector2, pointCount: int): bool // Check if point is within a polygon described by array of vertices +CheckCollisionLines :: #foreign (startPos1: Vector2, endPos1: Vector2, startPos2: Vector2, endPos2: Vector2, collisionPoint: *Vector2): bool // Check the collision between two lines defined by two points each, returns collision point by reference +CheckCollisionPointLine :: #foreign (point: Vector2, p1: Vector2, p2: Vector2, threshold: int): bool // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] +GetCollisionRec :: #foreign (rec1: Rectangle, rec2: Rectangle): Rectangle // Get collision rectangle for two rectangles collision + + + +// Image loading functions +// NOTE: These functions do not require GPU access + +LoadImage :: #foreign (fileName: *char): Image // Load image from file into CPU memory (RAM) +LoadImageRaw :: #foreign (fileName: *char, width: int, height: int, format: PixelFormat, headerSize: int): Image // Load image from RAW file data +LoadImageAnim :: #foreign (fileName: *char, frames: *int): Image // Load image sequence from file (frames appended to image.data) +LoadImageFromMemory :: #foreign (fileType: *char, fileData: *void, dataSize: int): Image // Load image from memory buffer, fileType refers to extension: i.e. '.png' +LoadImageFromTexture :: #foreign (texture: Texture2D): Image // Load image from GPU texture data +LoadImageFromScreen :: #foreign (): Image // Load image from screen buffer and (screenshot) +IsImageReady :: #foreign (image: Image): bool // Check if an image is ready +UnloadImage :: #foreign (image: Image) // Unload image from CPU memory (RAM) +ExportImage :: #foreign (image: Image, fileName: *char): bool // Export image data to file, returns true on success +ExportImageAsCode :: #foreign (image: Image, fileName: *char): bool // Export image as code file defining an array of bytes, returns true on success + +// Image generation functions + +GenImageColor :: #foreign (width: int, height: int, color: Color): Image // Generate image: plain color +GenImageGradientV :: #foreign (width: int, height: int, top: Color, bottom: Color): Image // Generate image: vertical gradient +GenImageGradientH :: #foreign (width: int, height: int, left: Color, right: Color): Image // Generate image: horizontal gradient +GenImageGradientRadial :: #foreign (width: int, height: int, density: F32, inner: Color, outer: Color): Image // Generate image: radial gradient +GenImageChecked :: #foreign (width: int, height: int, checksX: int, checksY: int, col1: Color, col2: Color): Image // Generate image: checked +GenImageWhiteNoise :: #foreign (width: int, height: int, factor: F32): Image // Generate image: white noise +GenImagePerlinNoise :: #foreign (width: int, height: int, offsetX: int, offsetY: int, scale: F32): Image // Generate image: perlin noise +GenImageCellular :: #foreign (width: int, height: int, tileSize: int): Image // Generate image: cellular algorithm, bigger tileSize means bigger cells +GenImageText :: #foreign (width: int, height: int, text: *char): Image // Generate image: grayscale image from text data + +// Image manipulation functions + +ImageCopy :: #foreign (image: Image): Image // Create an image duplicate (useful for transformations) +ImageFromImage :: #foreign (image: Image, rec: Rectangle): Image // Create an image from another image piece +ImageText :: #foreign (text: *char, fontSize: int, color: Color): Image // Create an image from text (default font) +ImageTextEx :: #foreign (font: Font, text: *char, fontSize: F32, spacing: F32, tint: Color): Image // Create an image from text (custom sprite font) +ImageFormat :: #foreign (image: *Image, newFormat: PixelFormat) // Convert image data to desired format +ImageToPOT :: #foreign (image: *Image, fill: Color) // Convert image to POT (power-of-two) +ImageCrop :: #foreign (image: *Image, crop: Rectangle) // Crop an image to a defined rectangle +ImageAlphaCrop :: #foreign (image: *Image, threshold: F32) // Crop image depending on alpha value +ImageAlphaClear :: #foreign (image: *Image, color: Color, threshold: F32) // Clear alpha channel to desired color +ImageAlphaMask :: #foreign (image: *Image, alphaMask: Image) // Apply alpha mask to image +ImageAlphaPremultiply :: #foreign (image: *Image) // Premultiply alpha channel +ImageBlurGaussian :: #foreign (image: *Image, blurSize: int) // Apply Gaussian blur using a box blur approximation +ImageResize :: #foreign (image: *Image, newWidth: int, newHeight: int) // Resize image (Bicubic scaling algorithm) +ImageResizeNN :: #foreign (image: *Image, newWidth: int, newHeight: int) // Resize image (Nearest-Neighbor scaling algorithm) +ImageResizeCanvas :: #foreign (image: *Image, newWidth: int, newHeight: int, offsetX: int, offsetY: int, fill: Color) // Resize canvas and fill with color +ImageMipmaps :: #foreign (image: *Image) // Compute all mipmap levels for a provided image +ImageDither :: #foreign (image: *Image, rBpp: int, gBpp: int, bBpp: int, aBpp: int) // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) +ImageFlipVertical :: #foreign (image: *Image) // Flip image vertically +ImageFlipHorizontal :: #foreign (image: *Image) // Flip image horizontally +ImageRotateCW :: #foreign (image: *Image) // Rotate image clockwise 90deg +ImageRotateCCW :: #foreign (image: *Image) // Rotate image counter-clockwise 90deg +ImageColorTint :: #foreign (image: *Image, color: Color) // Modify image color: tint +ImageColorInvert :: #foreign (image: *Image) // Modify image color: invert +ImageColorGrayscale :: #foreign (image: *Image) // Modify image color: grayscale +ImageColorContrast :: #foreign (image: *Image, contrast: F32) // Modify image color: contrast (-100 to 100) +ImageColorBrightness :: #foreign (image: *Image, brightness: int) // Modify image color: brightness (-255 to 255) +ImageColorReplace :: #foreign (image: *Image, color: Color, replace: Color) // Modify image color: replace color +LoadImageColors :: #foreign (image: Image): *Color // Load color data from image as a Color array (RGBA - 32bit) +LoadImagePalette :: #foreign (image: Image, maxPaletteSize: int, colorCount: *int): *Color // Load colors palette from image as a Color array (RGBA - 32bit) +UnloadImageColors :: #foreign (colors: *Color) // Unload color data loaded with LoadImageColors() +UnloadImagePalette :: #foreign (colors: *Color) // Unload colors palette loaded with LoadImagePalette() +GetImageAlphaBorder :: #foreign (image: Image, threshold: F32): Rectangle // Get image alpha border rectangle +GetImageColor :: #foreign (image: Image, x: int, y: int): Color // Get image pixel color at (x, y) position + +// Image drawing functions +// NOTE: Image software-rendering functions (CPU) +ImageClearBackground :: #foreign (dst: *Image, color: Color) // Clear image background with given color +ImageDrawPixel :: #foreign (dst: *Image, posX: int, posY: int, color: Color) // Draw pixel within an image +ImageDrawPixelV :: #foreign (dst: *Image, position: Vector2, color: Color) // Draw pixel within an image (Vector version) +ImageDrawLine :: #foreign (dst: *Image, startPosX: int, startPosY: int, endPosX: int, endPosY: int, color: Color) // Draw line within an image +ImageDrawLineV :: #foreign (dst: *Image, start: Vector2, end: Vector2, color: Color) // Draw line within an image (Vector version) +ImageDrawCircle :: #foreign (dst: *Image, centerX: int, centerY: int, radius: int, color: Color) // Draw a filled circle within an image +ImageDrawCircleV :: #foreign (dst: *Image, center: Vector2, radius: int, color: Color) // Draw a filled circle within an image (Vector version) +ImageDrawCircleLines :: #foreign (dst: *Image, centerX: int, centerY: int, radius: int, color: Color) // Draw circle outline within an image +ImageDrawCircleLinesV :: #foreign (dst: *Image, center: Vector2, radius: int, color: Color) // Draw circle outline within an image (Vector version) +ImageDrawRectangle :: #foreign (dst: *Image, posX: int, posY: int, width: int, height: int, color: Color) // Draw rectangle within an image +ImageDrawRectangleV :: #foreign (dst: *Image, position: Vector2, size: Vector2, color: Color) // Draw rectangle within an image (Vector version) +ImageDrawRectangleRec :: #foreign (dst: *Image, rec: Rectangle, color: Color) // Draw rectangle within an image +ImageDrawRectangleLines :: #foreign (dst: *Image, rec: Rectangle, thick: int, color: Color) // Draw rectangle lines within an image +ImageDraw :: #foreign (dst: *Image, src: Image, srcRec: Rectangle, dstRec: Rectangle, tint: Color) // Draw a source image within a destination image (tint applied to source) +ImageDrawText :: #foreign (dst: *Image, text: *char, posX: int, posY: int, fontSize: int, color: Color) // Draw text (using default font) within an image (destination) +ImageDrawTextEx :: #foreign (dst: *Image, font: Font, text: *char, position: Vector2, fontSize: F32, spacing: F32, tint: Color) // Draw text (custom sprite font) within an image (destination) + +// Texture loading functions +// NOTE: These functions require GPU access + +LoadTexture :: #foreign (fileName: *char): Texture2D // Load texture from file into GPU memory (VRAM) +LoadTextureFromImage :: #foreign (image: Image): Texture2D // Load texture from image data +LoadTextureCubemap :: #foreign (image: Image, layout: CubemapLayout): TextureCubemap // Load cubemap from image, multiple image cubemap layouts supported +LoadRenderTexture :: #foreign (width: int, height: int): RenderTexture2D // Load texture for rendering (framebuffer) +IsTextureReady :: #foreign (texture: Texture2D): bool // Check if a texture is ready +UnloadTexture :: #foreign (texture: Texture2D) // Unload texture from GPU memory (VRAM) +IsRenderTextureReady :: #foreign (target: RenderTexture2D): bool // Check if a render texture is ready +UnloadRenderTexture :: #foreign (target: RenderTexture2D) // Unload render texture from GPU memory (VRAM) +UpdateTexture :: #foreign (texture: Texture2D, pixels: *void) // Update GPU texture with new data +UpdateTextureRec :: #foreign (texture: Texture2D, rec: Rectangle, pixels: *void) // Update GPU texture rectangle with new data + +// Texture configuration functions + +GenTextureMipmaps :: #foreign (texture: *Texture2D) // Generate GPU mipmaps for a texture +SetTextureFilter :: #foreign (texture: Texture2D, filter: TextureFilter) // Set texture scaling filter mode +SetTextureWrap :: #foreign (texture: Texture2D, wrap: TextureWrap) // Set texture wrapping mode + + // Texture drawing functions +DrawTexture :: #foreign (texture: Texture2D, posX: int, posY: int, tint: Color) // Draw a Texture2D +DrawTextureV :: #foreign (texture: Texture2D, position: Vector2, tint: Color) // Draw a Texture2D with position defined as Vector2 +DrawTextureEx :: #foreign (texture: Texture2D, position: Vector2, rotation: F32, scale: F32, tint: Color) // Draw a Texture2D with extended parameters +DrawTextureRec :: #foreign (texture: Texture2D, source: Rectangle, position: Vector2, tint: Color) // Draw a part of a texture defined by a rectangle +DrawTexturePro :: #foreign (texture: Texture2D, source: Rectangle, dest: Rectangle, origin: Vector2, rotation: F32, tint: Color) // Draw a part of a texture defined by a rectangle with 'pro' parameters +DrawTextureNPatch :: #foreign (texture: Texture2D, nPatchInfo: NPatchInfo, dest: Rectangle, origin: Vector2, rotation: F32, tint: Color) // Draws a texture (or part of it) that stretches or shrinks nicely + +// Color/pixel related functions + +Fade :: #foreign (color: Color, alpha: F32): Color // Get color with alpha applied, alpha goes from 0.0f to 1.0f +ColorToInt :: #foreign (color: Color): uint // Get hexadecimal value for a Color +ColorNormalize :: #foreign (color: Color): Vector4 // Get Color normalized as float [0..1] +ColorFromNormalized :: #foreign (normalized: Vector4): Color // Get Color from normalized values [0..1] +ColorToHSV :: #foreign (color: Color): Vector3 // Get HSV values for a Color, hue [0..360], saturation/value [0..1] +ColorFromHSV :: #foreign (hue: F32, saturation: F32, value: F32): Color // Get a Color from HSV values, hue [0..360], saturation/value [0..1] +ColorTint :: #foreign (color: Color, tint: Color): Color // Get color multiplied with another color +ColorBrightness :: #foreign (color: Color, factor: F32): Color // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f +ColorContrast :: #foreign (color: Color, contrast: F32): Color // Get color with contrast correction, contrast values between -1.0f and 1.0f +ColorAlpha :: #foreign (color: Color, alpha: F32): Color // Get color with alpha applied, alpha goes from 0.0f to 1.0f +ColorAlphaBlend :: #foreign (dst: Color, src: Color, tint: Color): Color // Get src alpha-blended into dst color with tint +GetColor :: #foreign (hexValue: uint): Color // Get Color structure from hexadecimal value +GetPixelColor :: #foreign (srcPtr: *void, format: PixelFormat): Color // Get Color from a source pixel pointer of certain format +SetPixelColor :: #foreign (dstPtr: *void, color: Color, format: PixelFormat) // Set color formatted into destination pixel pointer +GetPixelDataSize :: #foreign (width: inr, height: int, format: PixelFormat): int // Get pixel data size in bytes for certain format + + + + +//------------------------------------------------------------------------------------ +// Font Loading and Text Drawing Functions (Module: text) +//------------------------------------------------------------------------------------ + +// Font loading/unloading functions + +GetFontDefault :: #foreign (): Font // Get the default Font +LoadFont :: #foreign (fileName: *char): Font // Load font from file into GPU memory (VRAM) +LoadFontEx :: #foreign (fileName: *char, fontSize: int, fontChars: *rune, glyphCount: int): Font // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set +LoadFontFromImage :: #foreign (image: Image, key: Color, firstChar: rune): Font // Load font from Image (XNA style) +LoadFontFromMemory :: #foreign (fileType: *char, fileData: *void, dataSize: int, fontSize: int, fontChars: *rune, glyphCount: int): Font // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' +IsFontReady :: #foreign (font: Font): bool // Check if a font is ready +LoadFontData :: #foreign (fileData: *void, dataSize: int, fontSize: int, fontChars: *rune, glyphCount: int, type: FontType): *GlyphInfo // Load font data for further use +GenImageFontAtlas :: #foreign (chars: *GlyphInfo, recs: **Rectangle, glyphCount: int, fontSize: int, padding: int, packMethod: int): Image // Generate image font atlas using chars info +UnloadFontData :: #foreign (chars: *GlyphInfo, glyphCount: int) // Unload font chars info data (RAM) +UnloadFont :: #foreign (font: Font) // Unload font from GPU memory (VRAM) +ExportFontAsCode :: #foreign (font: Font, fileName: *char): bool // Export font as code file, returns true on success + +// Text drawing functions + +DrawFPS :: #foreign (posX: int, posY: int) // Draw current FPS +DrawText :: #foreign (text: *char, posX: int, posY: int, fontSize: int, color: Color) // Draw text (using default font) +DrawTextEx :: #foreign (font: Font, text: *char, position: Vector2, fontSize: F32, spacing: F32, tint: Color) // Draw text using font and additional parameters +DrawTextPro :: #foreign (font: Font, text: *char, position: Vector2, origin: Vector2, rotation: F32, fontSize: F32, spacing: F32, tint: Color) // Draw text using Font and pro parameters (rotation) +DrawTextCodepoint :: #foreign (font: Font, codepoint: rune, position: Vector2, fontSize: F32, tint: Color) // Draw one character (codepoint) +DrawTextCodepoints :: #foreign (font: Font, codepoints: *rune, count: int, position: Vector2, fontSize: F32, spacing: F32, tint: Color) // Draw multiple character (codepoint) + +// Text font info functions + +MeasureText :: #foreign (text: *char, fontSize: int): int // Measure string width for default font +MeasureTextEx :: #foreign (font: Font, text: *char, fontSize: F32, spacing: F32): Vector2 // Measure string size for Font +GetGlyphIndex :: #foreign (font: Font, codepoint: rune): int // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found +GetGlyphInfo :: #foreign (font: Font, codepoint: rune): GlyphInfo // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found +GetGlyphAtlasRec :: #foreign (font: Font, codepoint: rune): Rectangle // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found + +// Text codepoints management functions (unicode characters) + +LoadUTF8 :: #foreign (codepoints: *rune, length: int): *uchar // Load UTF-8 text encoded from codepoints array +UnloadUTF8 :: #foreign (text: *uchar) // Unload UTF-8 text encoded from codepoints array +LoadCodepoints :: #foreign (text: *void, count: *int): *rune // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter +UnloadCodepoints :: #foreign (codepoints: *rune) // Unload codepoints data from memory +GetCodepointCount :: #foreign (text : *char): int // Get total number of codepoints in a UTF-8 encoded string +GetCodepoint :: #foreign (text: *char, codepointSize: *int): rune // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +GetCodepointNext :: #foreign (text: *char, codepointSize: *int): rune // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +GetCodepointPrevious :: #foreign (text: *char, codepointSize: *int): rune // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +CodepointToUTF8 :: #foreign (codepoint: rune, utf8Size: *int): *char // Encode one codepoint into UTF-8 byte array (array length returned as parameter) + +// Text strings management functions (no UTF-8 strings, only byte chars) +// NOTE: Some strings allocate memory internally for returned strings, just be careful! + +TextCopy :: #foreign (dst: *uchar, src: *char): int // Copy one string to another, returns bytes copied +TextIsEqual :: #foreign (text1: *char, text2: *char): bool // Check if two text string are equal +TextLength :: #foreign (text: *char): uint // Get text length, checks for '\0' ending + +// TextFormat is defined at the bottom of this file + +TextFormat :: #foreign (str: *char, ...): *char +TextSubtext :: #foreign (text: *char, position: int, length: int): *char // Get a piece of a text string +TextReplace :: #foreign (text: *uchar, replace: *char, by: *char): *uchar // Replace text string (WARNING: memory must be freed!) +TextInsert :: #foreign (text: *char, insert: *char, position: int): *uchar // Insert text in a position (WARNING: memory must be freed!) +TextJoin :: #foreign (textList: **char, count: int, delimiter: *char): *char // Join text strings with delimiter +TextSplit :: #foreign (text: *char, delimiter: byte, count: *int): **char // Split text into multiple strings +TextAppend :: #foreign (text: *uchar, append: *char, position: *int) // Append text at specific position and move cursor! +TextFindIndex :: #foreign (text: *char, find: *char): int // Find first text occurrence within a string +TextToUpper :: #foreign (text: *char): *char // Get upper case version of provided string +TextToLower :: #foreign (text: *char): *char // Get lower case version of provided string +TextToPascal :: #foreign (text: *char): *char // Get Pascal case notation version of provided string +TextToInteger :: #foreign (text: *char): int // Get integer value from text (negative values not supported) + + +//------------------------------------------------------------------------------------ +// Basic 3d Shapes Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ + +// Basic geometric 3D shapes drawing functions + +DrawLine3D :: #foreign (startPos: Vector3, endPos: Vector3, color: Color) // Draw a line in 3D world space +DrawPoint3D :: #foreign (position: Vector3, color: Color) // Draw a point in 3D space, actually a small line +DrawCircle3D :: #foreign (center: Vector3, radius: F32, rotationAxis: Vector3, rotationAngle: F32, color: Color) // Draw a circle in 3D world space +DrawTriangle3D :: #foreign (v1: Vector3, v2: Vector3, v3: Vector3, color: Color) // Draw a color-filled triangle (vertex in counter-clockwise order!) +DrawTriangleStrip3D :: #foreign (points: *Vector3, pointCount: int, color: Color) // Draw a triangle strip defined by points +DrawCube :: #foreign (position: Vector3, width: F32, height: F32, length: F32, color: Color) // Draw cube +DrawCubeV :: #foreign (position: Vector3, size: Vector3, color: Color) // Draw cube (Vector version) +DrawCubeWires :: #foreign (position: Vector3, width: F32, height: F32, length: F32, color: Color) // Draw cube wires +DrawCubeWiresV :: #foreign (position: Vector3, size: Vector3, color: Color) // Draw cube wires (Vector version) +DrawSphere :: #foreign (centerPos: Vector3, radius: F32, color: Color) // Draw sphere +DrawSphereEx :: #foreign (centerPos: Vector3, radius: F32, rings: int, slices: int, color: Color) // Draw sphere with extended parameters +DrawSphereWires :: #foreign (centerPos: Vector3, radius: F32, rings: int, slices: int, color: Color) // Draw sphere wires +DrawCylinder :: #foreign (position: Vector3, radiusTop: F32, radiusBottom: F32, height: F32, slices: int, color: Color) // Draw a cylinder/cone +DrawCylinderEx :: #foreign (startPos: Vector3, endPos: Vector3, startRadius: F32, endRadius: F32, sides: int, color: Color) // Draw a cylinder with base at startPos and top at endPos +DrawCylinderWires :: #foreign (position: Vector3, radiusTop: F32, radiusBottom: F32, height: F32, slices: int, color: Color) // Draw a cylinder/cone wires +DrawCylinderWiresEx :: #foreign (startPos: Vector3, endPos: Vector3, startRadius: F32, endRadius: F32, sides: int, color: Color) // Draw a cylinder wires with base at startPos and top at endPos +DrawCapsule :: #foreign (startPos: Vector3, endPos: Vector3, radius: F32, slices: int, rings: int, color: Color) // Draw a capsule with the center of its sphere caps at startPos and endPos +DrawCapsuleWires :: #foreign (startPos: Vector3, endPos: Vector3, radius: F32, slices: int, rings: int, color: Color) // Draw capsule wireframe with the center of its sphere caps at startPos and endPos +DrawPlane :: #foreign (centerPos: Vector3, size: Vector2, color: Color) // Draw a plane XZ +DrawRay :: #foreign (ray: Ray, color: Color) // Draw a ray line +DrawGrid :: #foreign (slices: int, spacing: F32) // Draw a grid (centered at (0, 0, 0)) + +//------------------------------------------------------------------------------------ +// Model 3d Loading and Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ + +// Model management functions + +LoadModel :: #foreign (fileName: *char): Model // Load model from files (meshes and materials) +LoadModelFromMesh :: #foreign (mesh: Mesh): Model // Load model from generated mesh (default material) +IsModelReady :: #foreign (model: Model): bool // Check if a model is ready +UnloadModel :: #foreign (model: Model) // Unload model (including meshes) from memory (RAM and/or VRAM) +GetModelBoundingBox :: #foreign (model: Model): BoundingBox // Compute model bounding box limits (considers all meshes) + +// Model drawing functions + +DrawModel :: #foreign (model: Model, position: Vector3, scale: F32, tint: Color) // Draw a model (with texture if set) +DrawModelEx :: #foreign (model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: F32, scale: Vector3, tint: Color) // Draw a model with extended parameters +DrawModelWires :: #foreign (model: Model, position: Vector3, scale: F32, tint: Color) // Draw a model wires (with texture if set) +DrawModelWiresEx :: #foreign (model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: F32, scale: Vector3, tint: Color) // Draw a model wires (with texture if set) with extended parameters +DrawBoundingBox :: #foreign (box: BoundingBox, color: Color) // Draw bounding box (wires) +DrawBillboard :: #foreign (camera: Camera, texture: Texture2D, position: Vector3, size: F32, tint: Color) // Draw a billboard texture +DrawBillboardRec :: #foreign (camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, size: Vector2, tint: Color) // Draw a billboard texture defined by source +DrawBillboardPro :: #foreign (camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, up: Vector3, size: Vector2, origin: Vector2, rotation: F32, tint: Color) // Draw a billboard texture defined by source and rotation + +// Mesh management functions + +UploadMesh :: #foreign (mesh: *Mesh, is_dynamic: bool) // Upload mesh vertex data in GPU and provide VAO/VBO ids +UpdateMeshBuffer :: #foreign (mesh: Mesh, index: int, data: *void, dataSize: int, offset: int) // Update mesh vertex data in GPU for a specific buffer index +UnloadMesh :: #foreign (mesh: Mesh) // Unload mesh data from CPU and GPU +DrawMesh :: #foreign (mesh: Mesh, material: Material, transform: Matrix) // Draw a 3d mesh with material and transform +DrawMeshInstanced :: #foreign (mesh: Mesh, material: Material, transforms: *Matrix, instances: int) // Draw multiple mesh instances with material and different transforms +ExportMesh :: #foreign (mesh: Mesh, fileName: *char): bool // Export mesh data to file, returns true on success +GetMeshBoundingBox :: #foreign (mesh: Mesh): BoundingBox // Compute mesh bounding box limits +GenMeshTangents :: #foreign (mesh: *Mesh) // Compute mesh tangents + +// Mesh generation functions + +GenMeshPoly :: #foreign (sides: int, radius: F32): Mesh // Generate polygonal mesh +GenMeshPlane :: #foreign (width: F32, lengthL: F32, resX: int, resZ: int): Mesh // Generate plane mesh (with subdivisions) +GenMeshCube :: #foreign (width: F32, height: F32, length: F32): Mesh // Generate cuboid mesh +GenMeshSphere :: #foreign (radius: F32, rings: int, slices: int): Mesh // Generate sphere mesh (standard sphere) +GenMeshHemiSphere :: #foreign (radius: F32, rings: int, slices: int): Mesh // Generate half-sphere mesh (no bottom cap) +GenMeshCylinder :: #foreign (radius: F32, height: F32, slices: int): Mesh // Generate cylinder mesh +GenMeshCone :: #foreign (radius: F32, height: F32, slices: int): Mesh // Generate cone/pyramid mesh +GenMeshTorus :: #foreign (radius: F32, size: F32, radSeg: int, sides: int): Mesh // Generate torus mesh +GenMeshKnot :: #foreign (radius: F32, size: F32, radSeg: int, sides: int): Mesh // Generate trefoil knot mesh +GenMeshHeightmap :: #foreign (heightmap: Image, size: Vector3): Mesh // Generate heightmap mesh from image data +GenMeshCubicmap :: #foreign (cubicmap: Image, cubeSize: Vector3): Mesh // Generate cubes-based map mesh from image data + +// Material loading/unloading functions + +LoadMaterials :: #foreign (fileName: *char, materialCount: *int): *Material // Load materials from model file +LoadMaterialDefault :: #foreign (): Material // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +IsMaterialReady :: #foreign (material: Material): bool // Check if a material is ready +UnloadMaterial :: #foreign (material: Material) // Unload material from GPU memory (VRAM) +SetMaterialTexture :: #foreign (material: *Material, mapType: MaterialMapIndex, texture: Texture2D) // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) +SetModelMeshMaterial :: #foreign (model: *Model, meshId: int, materialId: int) // Set material for a mesh + +// Model animations loading/unloading functions + +LoadModelAnimations :: #foreign (fileName: *char, animCount: *uint): *ModelAnimation // Load model animations from file +UpdateModelAnimation :: #foreign (model: Model, anim: ModelAnimation, frame: int) // Update model animation pose +UnloadModelAnimation :: #foreign (anim: ModelAnimation) // Unload animation data +UnloadModelAnimations :: #foreign (animations: *ModelAnimation, count: uint) // Unload animation array data +IsModelAnimationValid :: #foreign (model: Model, anim: ModelAnimation): bool // Check model animation skeleton match + +// Collision detection functions + +CheckCollisionSpheres :: #foreign (center1: Vector3, radius1: F32, center2: Vector3, radius2: F32): bool // Check collision between two spheres +CheckCollisionBoxes :: #foreign (box1: BoundingBox, box2: BoundingBox): bool // Check collision between two bounding boxes +CheckCollisionBoxSphere :: #foreign (box: BoundingBox, center: Vector3, radius: F32): bool // Check collision between box and sphere +GetRayCollisionSphere :: #foreign (ray: Ray, center: Vector3, radius: F32): RayCollision // Get collision info between ray and sphere +GetRayCollisionBox :: #foreign (ray: Ray, box: BoundingBox): RayCollision // Get collision info between ray and box +GetRayCollisionMesh :: #foreign (ray: Ray, mesh: Mesh, transform: Matrix): RayCollision // Get collision info between ray and mesh +GetRayCollisionTriangle :: #foreign (ray: Ray, p1: Vector3, p2: Vector3, p3: Vector3): RayCollision // Get collision info between ray and triangle +GetRayCollisionQuad :: #foreign (ray: Ray, p1: Vector4, p2: Vector4, p3: Vector4, p4: Vector3): RayCollision // Get collision info between ray and quad + +//------------------------------------------------------------------------------------ +// Audio Loading and Playing Functions (Module: audio) +//------------------------------------------------------------------------------------ + +// Audio device management functions + +InitAudioDevice :: #foreign () // Initialize audio device and context +CloseAudioDevice :: #foreign () // Close the audio device and context +IsAudioDeviceReady :: #foreign (): bool // Check if audio device has been initialized successfully +SetMasterVolume :: #foreign (volume: F32) // Set master volume (listener) + +// Wave/Sound loading/unloading functions + +LoadWave :: #foreign (fileName: *char): Wave // Load wave data from file +LoadWaveFromMemory :: #foreign (fileType: *char, fileData: *void, dataSize: int): Wave // Load wave from memory buffer, fileType refers to extension: i.e. '.wav' +IsWaveReady :: #foreign (wave: Wave): bool // Checks if wave data is ready +LoadSound :: #foreign (fileName: *char): Sound // Load sound from file +LoadSoundFromWave :: #foreign (wave: Wave): Sound // Load sound from wave data +IsSoundReady :: #foreign (sound: Sound): bool // Checks if a sound is ready +UpdateSound :: #foreign (sound: Sound, data: *void, sampleCount: int) // Update sound buffer with new data +UnloadWave :: #foreign (wave: Wave) // Unload wave data +UnloadSound :: #foreign (sound: Sound) // Unload sound +ExportWave :: #foreign (wave: Wave, fileName: *char): bool // Export wave data to file, returns true on success +ExportWaveAsCode :: #foreign (wave: Wave, fileName: *char): bool // Export wave sample data to code (.h), returns true on success + +// Wave/Sound management functions + +PlaySound :: #foreign (sound: Sound) // Play a sound +StopSound :: #foreign (sound: Sound) // Stop playing a sound +PauseSound :: #foreign (sound: Sound) // Pause a sound +ResumeSound :: #foreign (sound: Sound) // Resume a paused sound +IsSoundPlaying :: #foreign (sound: Sound): bool // Check if a sound is currently playing +SetSoundVolume :: #foreign (sound: Sound, volume: F32) // Set volume for a sound (1.0 is max level) +SetSoundPitch :: #foreign (sound: Sound, pitch: F32) // Set pitch for a sound (1.0 is base level) +SetSoundPan :: #foreign (sound: Sound, pan: F32) // Set pan for a sound (0.5 is center) +WaveCopy :: #foreign (wave: Wave): Wave // Copy a wave to a new wave +WaveCrop :: #foreign (wave: *Wave, initSample: int, finalSample: int) // Crop a wave to defined samples range +WaveFormat :: #foreign (wave: *Wave, sampleRate: int, sampleSize: int, channels: int) // Convert wave data to desired format +LoadWaveSamples :: #foreign (wave: Wave): *F32 // Load samples data from wave as a 32bit float data array +UnloadWaveSamples :: #foreign (samples: *F32) // Unload samples data loaded with LoadWaveSamples() + +// Music management functions + +LoadMusicStream :: #foreign (fileName: *char): Music // Load music stream from file +LoadMusicStreamFromMemory :: #foreign (fileType: *char, data: *void, dataSize: int): Music // Load music stream from data +IsMusicReady :: #foreign (music: Music): bool // Checks if a music stream is ready +UnloadMusicStream :: #foreign (music: Music) // Unload music stream +PlayMusicStream :: #foreign (music: Music) // Start music playing +IsMusicStreamPlaying :: #foreign (music: Music): bool // Check if music is playing +UpdateMusicStream :: #foreign (music: Music) // Updates buffers for music streaming +StopMusicStream :: #foreign (music: Music) // Stop music playing +PauseMusicStream :: #foreign (music: Music) // Pause music playing +ResumeMusicStream :: #foreign (music: Music) // Resume playing paused music +SeekMusicStream :: #foreign (music: Music, position: F32) // Seek music to a position (in seconds) +SetMusicVolume :: #foreign (music: Music, volume: F32) // Set volume for music (1.0 is max level) +SetMusicPitch :: #foreign (music: Music, pitch: F32) // Set pitch for a music (1.0 is base level) +SetMusicPan :: #foreign (music: Music, pan: F32) // Set pan for a music (0.5 is center) +GetMusicTimeLength :: #foreign (music: Music): F32 // Get music time length (in seconds) +GetMusicTimePlayed :: #foreign (music: Music): F32 // Get current music time played (in seconds) + +// AudioStream management functions + +LoadAudioStream :: #foreign (sampleRate: uint, sampleSize: uint, channels: uint): AudioStream // Load audio stream (to stream raw audio pcm data) +IsAudioStreamReady :: #foreign (stream: AudioStream): bool // Checks if an audio stream is ready +UnloadAudioStream :: #foreign (stream: AudioStream) // Unload audio stream and free memory +UpdateAudioStream :: #foreign (stream: AudioStream, data: *void, frameCount: int) // Update audio stream buffers with data +IsAudioStreamProcessed :: #foreign (stream: AudioStream): bool // Check if any audio stream buffers requires refill +PlayAudioStream :: #foreign (stream: AudioStream) // Play audio stream +PauseAudioStream :: #foreign (stream: AudioStream) // Pause audio stream +ResumeAudioStream :: #foreign (stream: AudioStream) // Resume audio stream +IsAudioStreamPlaying :: #foreign (stream: AudioStream): bool // Check if audio stream is playing +StopAudioStream :: #foreign (stream: AudioStream) // Stop audio stream +SetAudioStreamVolume :: #foreign (stream: AudioStream, volume: F32) // Set volume for audio stream (1.0 is max level) +SetAudioStreamPitch :: #foreign (stream: AudioStream, pitch: F32) // Set pitch for audio stream (1.0 is base level) +SetAudioStreamPan :: #foreign (stream: AudioStream, pan: F32) // Set pan for audio stream (0.5 is centered) +SetAudioStreamBufferSizeDefault :: #foreign (size: int) // Default size for new audio streams +SetAudioStreamCallback :: #foreign (stream: AudioStream, callback: AudioCallback) // Audio thread callback to request new data + +AttachAudioStreamProcessor :: #foreign (stream: AudioStream, processor: AudioCallback) // Attach audio stream processor to stream +DetachAudioStreamProcessor :: #foreign (stream: AudioStream, processor: AudioCallback) // Detach audio stream processor from stream + +AttachAudioMixedProcessor :: #foreign (processor: AudioCallback) // Attach audio stream processor to the entire audio pipeline +DetachAudioMixedProcessor :: #foreign (processor: AudioCallback) // Detach audio stream processor from the entire audio pipeline diff --git a/build/vendor/raylib/raylib.h b/modules/vendor/raylib/raylib.h similarity index 98% rename from build/vendor/raylib/raylib.h rename to modules/vendor/raylib/raylib.h index 4cd9e43..6c79d6d 100644 --- a/build/vendor/raylib/raylib.h +++ b/modules/vendor/raylib/raylib.h @@ -1,1588 +1,1588 @@ -/********************************************************************************************** -* -* raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) -* -* FEATURES: -* - NO external dependencies, all required libraries included with raylib -* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, -* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5. -* - Written in plain C code (C99) in PascalCase/camelCase notation -* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3 or ES2 - choose at compile) -* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] -* - Multiple Fonts formats supported (TTF, XNA fonts, AngelCode fonts) -* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) -* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! -* - Flexible Materials system, supporting classic maps and PBR maps -* - Animated 3D models supported (skeletal bones animation) (IQM) -* - Shaders support, including Model shaders and Postprocessing shaders -* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] -* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) -* - VR stereo rendering with configurable HMD device parameters -* - Bindings to multiple programming languages available! -* -* NOTES: -* - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text] -* - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2) -* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2) -* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2) -* -* DEPENDENCIES (included): -* [rcore] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input (PLATFORM_DESKTOP) -* [rlgl] glad (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (PLATFORM_DESKTOP) -* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management -* -* OPTIONAL DEPENDENCIES (included): -* [rcore] msf_gif (Miles Fogle) for GIF recording -* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm -* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm -* [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) -* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) -* [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms -* [rtext] stb_truetype (Sean Barret) for ttf fonts loading -* [rtext] stb_rect_pack (Sean Barret) for rectangles packing -* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation -* [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) -* [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF) -* [rmodels] Model3D (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d) -* [raudio] dr_wav (David Reid) for WAV audio file loading -* [raudio] dr_flac (David Reid) for FLAC audio file loading -* [raudio] dr_mp3 (David Reid) for MP3 audio file loading -* [raudio] stb_vorbis (Sean Barret) for OGG audio loading -* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading -* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading -* -* -* LICENSE: zlib/libpng -* -* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, -* BSD-like license that allows static linking with closed source software: -* -* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RAYLIB_H -#define RAYLIB_H - -#include // Required for: va_list - Only used by TraceLogCallback - -#define RAYLIB_VERSION_MAJOR 4 -#define RAYLIB_VERSION_MINOR 5 -#define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "4.5" - -// Function specifiers in case library is build/used as a shared library (Windows) -// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll -#if defined(_WIN32) - #if defined(BUILD_LIBTYPE_SHARED) - #if defined(__TINYC__) - #define __declspec(x) __attribute__((x)) - #endif - #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) - #elif defined(USE_LIBTYPE_SHARED) - #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) - #endif -#endif - -#ifndef RLAPI - #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) -#endif - -//---------------------------------------------------------------------------------- -// Some basic Defines -//---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846f -#endif -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif - -// Allow custom memory allocators -// NOTE: Require recompiling raylib sources -#ifndef RL_MALLOC - #define RL_MALLOC(sz) malloc(sz) -#endif -#ifndef RL_CALLOC - #define RL_CALLOC(n,sz) calloc(n,sz) -#endif -#ifndef RL_REALLOC - #define RL_REALLOC(ptr,sz) realloc(ptr,sz) -#endif -#ifndef RL_FREE - #define RL_FREE(ptr) free(ptr) -#endif - -// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) -// Plain structures in C++ (without constructors) can be initialized with { } -#if defined(__cplusplus) - #define CLITERAL(type) type -#else - #define CLITERAL(type) (type) -#endif - -// NOTE: We set some defines with some data types declared by raylib -// Other modules (raymath, rlgl) also require some of those types, so, -// to be able to use those other modules as standalone (not depending on raylib) -// this defines are very useful for internal check and avoid type (re)definitions -#define RL_COLOR_TYPE -#define RL_RECTANGLE_TYPE -#define RL_VECTOR2_TYPE -#define RL_VECTOR3_TYPE -#define RL_VECTOR4_TYPE -#define RL_QUATERNION_TYPE -#define RL_MATRIX_TYPE - -// Some Basic Colors -// NOTE: Custom raylib color palette for amazing visuals on WHITE background -#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray -#define GRAY CLITERAL(Color){ 130, 130, 130, 255 } // Gray -#define DARKGRAY CLITERAL(Color){ 80, 80, 80, 255 } // Dark Gray -#define YELLOW CLITERAL(Color){ 253, 249, 0, 255 } // Yellow -#define GOLD CLITERAL(Color){ 255, 203, 0, 255 } // Gold -#define ORANGE CLITERAL(Color){ 255, 161, 0, 255 } // Orange -#define PINK CLITERAL(Color){ 255, 109, 194, 255 } // Pink -#define RED CLITERAL(Color){ 230, 41, 55, 255 } // Red -#define MAROON CLITERAL(Color){ 190, 33, 55, 255 } // Maroon -#define GREEN CLITERAL(Color){ 0, 228, 48, 255 } // Green -#define LIME CLITERAL(Color){ 0, 158, 47, 255 } // Lime -#define DARKGREEN CLITERAL(Color){ 0, 117, 44, 255 } // Dark Green -#define SKYBLUE CLITERAL(Color){ 102, 191, 255, 255 } // Sky Blue -#define BLUE CLITERAL(Color){ 0, 121, 241, 255 } // Blue -#define DARKBLUE CLITERAL(Color){ 0, 82, 172, 255 } // Dark Blue -#define PURPLE CLITERAL(Color){ 200, 122, 255, 255 } // Purple -#define VIOLET CLITERAL(Color){ 135, 60, 190, 255 } // Violet -#define DARKPURPLE CLITERAL(Color){ 112, 31, 126, 255 } // Dark Purple -#define BEIGE CLITERAL(Color){ 211, 176, 131, 255 } // Beige -#define BROWN CLITERAL(Color){ 127, 106, 79, 255 } // Brown -#define DARKBROWN CLITERAL(Color){ 76, 63, 47, 255 } // Dark Brown - -#define WHITE CLITERAL(Color){ 255, 255, 255, 255 } // White -#define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black -#define BLANK CLITERAL(Color){ 0, 0, 0, 0 } // Blank (Transparent) -#define MAGENTA CLITERAL(Color){ 255, 0, 255, 255 } // Magenta -#define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 } // My own White (raylib logo) - -//---------------------------------------------------------------------------------- -// Structures Definition -//---------------------------------------------------------------------------------- -// Boolean type -#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) - #include -#elif !defined(__cplusplus) && !defined(bool) - typedef enum bool { false = 0, true = !false } bool; - #define RL_BOOL_TYPE -#endif - -// Vector2, 2 components -typedef struct Vector2 { - float x; // Vector x component - float y; // Vector y component -} Vector2; - -// Vector3, 3 components -typedef struct Vector3 { - float x; // Vector x component - float y; // Vector y component - float z; // Vector z component -} Vector3; - -// Vector4, 4 components -typedef struct Vector4 { - float x; // Vector x component - float y; // Vector y component - float z; // Vector z component - float w; // Vector w component -} Vector4; - -// Quaternion, 4 components (Vector4 alias) -typedef Vector4 Quaternion; - -// Matrix, 4x4 components, column major, OpenGL style, right-handed -typedef struct Matrix { - float m0, m4, m8, m12; // Matrix first row (4 components) - float m1, m5, m9, m13; // Matrix second row (4 components) - float m2, m6, m10, m14; // Matrix third row (4 components) - float m3, m7, m11, m15; // Matrix fourth row (4 components) -} Matrix; - -// Color, 4 components, R8G8B8A8 (32bit) -typedef struct Color { - unsigned char r; // Color red value - unsigned char g; // Color green value - unsigned char b; // Color blue value - unsigned char a; // Color alpha value -} Color; - -// Rectangle, 4 components -typedef struct Rectangle { - float x; // Rectangle top-left corner position x - float y; // Rectangle top-left corner position y - float width; // Rectangle width - float height; // Rectangle height -} Rectangle; - -// Image, pixel data stored in CPU memory (RAM) -typedef struct Image { - void *data; // Image raw data - int width; // Image base width - int height; // Image base height - int mipmaps; // Mipmap levels, 1 by default - int format; // Data format (PixelFormat type) -} Image; - -// Texture, tex data stored in GPU memory (VRAM) -typedef struct Texture { - unsigned int id; // OpenGL texture id - int width; // Texture base width - int height; // Texture base height - int mipmaps; // Mipmap levels, 1 by default - int format; // Data format (PixelFormat type) -} Texture; - -// Texture2D, same as Texture -typedef Texture Texture2D; - -// TextureCubemap, same as Texture -typedef Texture TextureCubemap; - -// RenderTexture, fbo for texture rendering -typedef struct RenderTexture { - unsigned int id; // OpenGL framebuffer object id - Texture texture; // Color buffer attachment texture - Texture depth; // Depth buffer attachment texture -} RenderTexture; - -// RenderTexture2D, same as RenderTexture -typedef RenderTexture RenderTexture2D; - -// NPatchInfo, n-patch layout info -typedef struct NPatchInfo { - Rectangle source; // Texture source rectangle - int left; // Left border offset - int top; // Top border offset - int right; // Right border offset - int bottom; // Bottom border offset - int layout; // Layout of the n-patch: 3x3, 1x3 or 3x1 -} NPatchInfo; - -// GlyphInfo, font characters glyphs info -typedef struct GlyphInfo { - int value; // Character value (Unicode) - int offsetX; // Character offset X when drawing - int offsetY; // Character offset Y when drawing - int advanceX; // Character advance position X - Image image; // Character image data -} GlyphInfo; - -// Font, font texture and GlyphInfo array data -typedef struct Font { - int baseSize; // Base size (default chars height) - int glyphCount; // Number of glyph characters - int glyphPadding; // Padding around the glyph characters - Texture2D texture; // Texture atlas containing the glyphs - Rectangle *recs; // Rectangles in texture for the glyphs - GlyphInfo *glyphs; // Glyphs info data -} Font; - -// Camera, defines position/orientation in 3d space -typedef struct Camera3D { - Vector3 position; // Camera position - Vector3 target; // Camera target it looks-at - Vector3 up; // Camera up vector (rotation over its axis) - float fovy; // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic - int projection; // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC -} Camera3D; - -typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D - -// Camera2D, defines position/orientation in 2d space -typedef struct Camera2D { - Vector2 offset; // Camera offset (displacement from target) - Vector2 target; // Camera target (rotation and zoom origin) - float rotation; // Camera rotation in degrees - float zoom; // Camera zoom (scaling), should be 1.0f by default -} Camera2D; - -// Mesh, vertex data and vao/vbo -typedef struct Mesh { - int vertexCount; // Number of vertices stored in arrays - int triangleCount; // Number of triangles stored (indexed or not) - - // Vertex attributes data - float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) - float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) - float *texcoords2; // Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5) - float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) - float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) - unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) - unsigned short *indices; // Vertex indices (in case vertex data comes indexed) - - // Animation vertex data - float *animVertices; // Animated vertex positions (after bones transformations) - float *animNormals; // Animated normals (after bones transformations) - unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) - float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) - - // OpenGL identifiers - unsigned int vaoId; // OpenGL Vertex Array Object id - unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data) -} Mesh; - -// Shader -typedef struct Shader { - unsigned int id; // Shader program id - int *locs; // Shader locations array (RL_MAX_SHADER_LOCATIONS) -} Shader; - -// MaterialMap -typedef struct MaterialMap { - Texture2D texture; // Material map texture - Color color; // Material map color - float value; // Material map value -} MaterialMap; - -// Material, includes shader and maps -typedef struct Material { - Shader shader; // Material shader - MaterialMap *maps; // Material maps array (MAX_MATERIAL_MAPS) - float params[4]; // Material generic parameters (if required) -} Material; - -// Transform, vertex transformation data -typedef struct Transform { - Vector3 translation; // Translation - Quaternion rotation; // Rotation - Vector3 scale; // Scale -} Transform; - -// Bone, skeletal animation bone -typedef struct BoneInfo { - char name[32]; // Bone name - int parent; // Bone parent -} BoneInfo; - -// Model, meshes, materials and animation data -typedef struct Model { - Matrix transform; // Local transform matrix - - int meshCount; // Number of meshes - int materialCount; // Number of materials - Mesh *meshes; // Meshes array - Material *materials; // Materials array - int *meshMaterial; // Mesh material number - - // Animation data - int boneCount; // Number of bones - BoneInfo *bones; // Bones information (skeleton) - Transform *bindPose; // Bones base transformation (pose) -} Model; - -// ModelAnimation -typedef struct ModelAnimation { - int boneCount; // Number of bones - int frameCount; // Number of animation frames - BoneInfo *bones; // Bones information (skeleton) - Transform **framePoses; // Poses array by frame -} ModelAnimation; - -// Ray, ray for raycasting -typedef struct Ray { - Vector3 position; // Ray position (origin) - Vector3 direction; // Ray direction -} Ray; - -// RayCollision, ray hit information -typedef struct RayCollision { - bool hit; // Did the ray hit something? - float distance; // Distance to the nearest hit - Vector3 point; // Point of the nearest hit - Vector3 normal; // Surface normal of hit -} RayCollision; - -// BoundingBox -typedef struct BoundingBox { - Vector3 min; // Minimum vertex box-corner - Vector3 max; // Maximum vertex box-corner -} BoundingBox; - -// Wave, audio wave data -typedef struct Wave { - unsigned int frameCount; // Total number of frames (considering channels) - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo, ...) - void *data; // Buffer data pointer -} Wave; - -// Opaque structs declaration -// NOTE: Actual structs are defined internally in raudio module -typedef struct rAudioBuffer rAudioBuffer; -typedef struct rAudioProcessor rAudioProcessor; - -// AudioStream, custom audio stream -typedef struct AudioStream { - rAudioBuffer *buffer; // Pointer to internal data used by the audio system - rAudioProcessor *processor; // Pointer to internal data processor, useful for audio effects - - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo, ...) -} AudioStream; - -// Sound -typedef struct Sound { - AudioStream stream; // Audio stream - unsigned int frameCount; // Total number of frames (considering channels) -} Sound; - -// Music, audio stream, anything longer than ~10 seconds should be streamed -typedef struct Music { - AudioStream stream; // Audio stream - unsigned int frameCount; // Total number of frames (considering channels) - bool looping; // Music looping enable - - int ctxType; // Type of music context (audio filetype) - void *ctxData; // Audio context data, depends on type -} Music; - -// VrDeviceInfo, Head-Mounted-Display device parameters -typedef struct VrDeviceInfo { - int hResolution; // Horizontal resolution in pixels - int vResolution; // Vertical resolution in pixels - float hScreenSize; // Horizontal size in meters - float vScreenSize; // Vertical size in meters - float vScreenCenter; // Screen center in meters - float eyeToScreenDistance; // Distance between eye and display in meters - float lensSeparationDistance; // Lens separation distance in meters - float interpupillaryDistance; // IPD (distance between pupils) in meters - float lensDistortionValues[4]; // Lens distortion constant parameters - float chromaAbCorrection[4]; // Chromatic aberration correction parameters -} VrDeviceInfo; - -// VrStereoConfig, VR stereo rendering configuration for simulator -typedef struct VrStereoConfig { - Matrix projection[2]; // VR projection matrices (per eye) - Matrix viewOffset[2]; // VR view offset matrices (per eye) - float leftLensCenter[2]; // VR left lens center - float rightLensCenter[2]; // VR right lens center - float leftScreenCenter[2]; // VR left screen center - float rightScreenCenter[2]; // VR right screen center - float scale[2]; // VR distortion scale - float scaleIn[2]; // VR distortion scale in -} VrStereoConfig; - -// File path list -typedef struct FilePathList { - unsigned int capacity; // Filepaths max entries - unsigned int count; // Filepaths entries count - char **paths; // Filepaths entries -} FilePathList; - -//---------------------------------------------------------------------------------- -// Enumerators Definition -//---------------------------------------------------------------------------------- -// System/Window config flags -// NOTE: Every bit registers one state (use it with bit masks) -// By default all flags are set to 0 -typedef enum { - FLAG_VSYNC_HINT = 0x00000040, // Set to try enabling V-Sync on GPU - FLAG_FULLSCREEN_MODE = 0x00000002, // Set to run program in fullscreen - FLAG_WINDOW_RESIZABLE = 0x00000004, // Set to allow resizable window - FLAG_WINDOW_UNDECORATED = 0x00000008, // Set to disable window decoration (frame and buttons) - FLAG_WINDOW_HIDDEN = 0x00000080, // Set to hide window - FLAG_WINDOW_MINIMIZED = 0x00000200, // Set to minimize window (iconify) - FLAG_WINDOW_MAXIMIZED = 0x00000400, // Set to maximize window (expanded to monitor) - FLAG_WINDOW_UNFOCUSED = 0x00000800, // Set to window non focused - FLAG_WINDOW_TOPMOST = 0x00001000, // Set to window always on top - FLAG_WINDOW_ALWAYS_RUN = 0x00000100, // Set to allow windows running while minimized - FLAG_WINDOW_TRANSPARENT = 0x00000010, // Set to allow transparent framebuffer - FLAG_WINDOW_HIGHDPI = 0x00002000, // Set to support HighDPI - FLAG_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED - FLAG_MSAA_4X_HINT = 0x00000020, // Set to try enabling MSAA 4X - FLAG_INTERLACED_HINT = 0x00010000 // Set to try enabling interlaced video format (for V3D) -} ConfigFlags; - -// Trace log level -// NOTE: Organized by priority level -typedef enum { - LOG_ALL = 0, // Display all logs - LOG_TRACE, // Trace logging, intended for internal use only - LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds - LOG_INFO, // Info logging, used for program execution info - LOG_WARNING, // Warning logging, used on recoverable failures - LOG_ERROR, // Error logging, used on unrecoverable failures - LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) - LOG_NONE // Disable logging -} TraceLogLevel; - -// Keyboard keys (US keyboard layout) -// NOTE: Use GetKeyPressed() to allow redefining -// required keys for alternative layouts -typedef enum { - KEY_NULL = 0, // Key: NULL, used for no key pressed - // Alphanumeric keys - KEY_APOSTROPHE = 39, // Key: ' - KEY_COMMA = 44, // Key: , - KEY_MINUS = 45, // Key: - - KEY_PERIOD = 46, // Key: . - KEY_SLASH = 47, // Key: / - KEY_ZERO = 48, // Key: 0 - KEY_ONE = 49, // Key: 1 - KEY_TWO = 50, // Key: 2 - KEY_THREE = 51, // Key: 3 - KEY_FOUR = 52, // Key: 4 - KEY_FIVE = 53, // Key: 5 - KEY_SIX = 54, // Key: 6 - KEY_SEVEN = 55, // Key: 7 - KEY_EIGHT = 56, // Key: 8 - KEY_NINE = 57, // Key: 9 - KEY_SEMICOLON = 59, // Key: ; - KEY_EQUAL = 61, // Key: = - KEY_A = 65, // Key: A | a - KEY_B = 66, // Key: B | b - KEY_C = 67, // Key: C | c - KEY_D = 68, // Key: D | d - KEY_E = 69, // Key: E | e - KEY_F = 70, // Key: F | f - KEY_G = 71, // Key: G | g - KEY_H = 72, // Key: H | h - KEY_I = 73, // Key: I | i - KEY_J = 74, // Key: J | j - KEY_K = 75, // Key: K | k - KEY_L = 76, // Key: L | l - KEY_M = 77, // Key: M | m - KEY_N = 78, // Key: N | n - KEY_O = 79, // Key: O | o - KEY_P = 80, // Key: P | p - KEY_Q = 81, // Key: Q | q - KEY_R = 82, // Key: R | r - KEY_S = 83, // Key: S | s - KEY_T = 84, // Key: T | t - KEY_U = 85, // Key: U | u - KEY_V = 86, // Key: V | v - KEY_W = 87, // Key: W | w - KEY_X = 88, // Key: X | x - KEY_Y = 89, // Key: Y | y - KEY_Z = 90, // Key: Z | z - KEY_LEFT_BRACKET = 91, // Key: [ - KEY_BACKSLASH = 92, // Key: '\' - KEY_RIGHT_BRACKET = 93, // Key: ] - KEY_GRAVE = 96, // Key: ` - // Function keys - KEY_SPACE = 32, // Key: Space - KEY_ESCAPE = 256, // Key: Esc - KEY_ENTER = 257, // Key: Enter - KEY_TAB = 258, // Key: Tab - KEY_BACKSPACE = 259, // Key: Backspace - KEY_INSERT = 260, // Key: Ins - KEY_DELETE = 261, // Key: Del - KEY_RIGHT = 262, // Key: Cursor right - KEY_LEFT = 263, // Key: Cursor left - KEY_DOWN = 264, // Key: Cursor down - KEY_UP = 265, // Key: Cursor up - KEY_PAGE_UP = 266, // Key: Page up - KEY_PAGE_DOWN = 267, // Key: Page down - KEY_HOME = 268, // Key: Home - KEY_END = 269, // Key: End - KEY_CAPS_LOCK = 280, // Key: Caps lock - KEY_SCROLL_LOCK = 281, // Key: Scroll down - KEY_NUM_LOCK = 282, // Key: Num lock - KEY_PRINT_SCREEN = 283, // Key: Print screen - KEY_PAUSE = 284, // Key: Pause - KEY_F1 = 290, // Key: F1 - KEY_F2 = 291, // Key: F2 - KEY_F3 = 292, // Key: F3 - KEY_F4 = 293, // Key: F4 - KEY_F5 = 294, // Key: F5 - KEY_F6 = 295, // Key: F6 - KEY_F7 = 296, // Key: F7 - KEY_F8 = 297, // Key: F8 - KEY_F9 = 298, // Key: F9 - KEY_F10 = 299, // Key: F10 - KEY_F11 = 300, // Key: F11 - KEY_F12 = 301, // Key: F12 - KEY_LEFT_SHIFT = 340, // Key: Shift left - KEY_LEFT_CONTROL = 341, // Key: Control left - KEY_LEFT_ALT = 342, // Key: Alt left - KEY_LEFT_SUPER = 343, // Key: Super left - KEY_RIGHT_SHIFT = 344, // Key: Shift right - KEY_RIGHT_CONTROL = 345, // Key: Control right - KEY_RIGHT_ALT = 346, // Key: Alt right - KEY_RIGHT_SUPER = 347, // Key: Super right - KEY_KB_MENU = 348, // Key: KB menu - // Keypad keys - KEY_KP_0 = 320, // Key: Keypad 0 - KEY_KP_1 = 321, // Key: Keypad 1 - KEY_KP_2 = 322, // Key: Keypad 2 - KEY_KP_3 = 323, // Key: Keypad 3 - KEY_KP_4 = 324, // Key: Keypad 4 - KEY_KP_5 = 325, // Key: Keypad 5 - KEY_KP_6 = 326, // Key: Keypad 6 - KEY_KP_7 = 327, // Key: Keypad 7 - KEY_KP_8 = 328, // Key: Keypad 8 - KEY_KP_9 = 329, // Key: Keypad 9 - KEY_KP_DECIMAL = 330, // Key: Keypad . - KEY_KP_DIVIDE = 331, // Key: Keypad / - KEY_KP_MULTIPLY = 332, // Key: Keypad * - KEY_KP_SUBTRACT = 333, // Key: Keypad - - KEY_KP_ADD = 334, // Key: Keypad + - KEY_KP_ENTER = 335, // Key: Keypad Enter - KEY_KP_EQUAL = 336, // Key: Keypad = - // Android key buttons - KEY_BACK = 4, // Key: Android back button - KEY_MENU = 82, // Key: Android menu button - KEY_VOLUME_UP = 24, // Key: Android volume up button - KEY_VOLUME_DOWN = 25 // Key: Android volume down button -} KeyboardKey; - -// Add backwards compatibility support for deprecated names -#define MOUSE_LEFT_BUTTON MOUSE_BUTTON_LEFT -#define MOUSE_RIGHT_BUTTON MOUSE_BUTTON_RIGHT -#define MOUSE_MIDDLE_BUTTON MOUSE_BUTTON_MIDDLE - -// Mouse buttons -typedef enum { - MOUSE_BUTTON_LEFT = 0, // Mouse button left - MOUSE_BUTTON_RIGHT = 1, // Mouse button right - MOUSE_BUTTON_MIDDLE = 2, // Mouse button middle (pressed wheel) - MOUSE_BUTTON_SIDE = 3, // Mouse button side (advanced mouse device) - MOUSE_BUTTON_EXTRA = 4, // Mouse button extra (advanced mouse device) - MOUSE_BUTTON_FORWARD = 5, // Mouse button forward (advanced mouse device) - MOUSE_BUTTON_BACK = 6, // Mouse button back (advanced mouse device) -} MouseButton; - -// Mouse cursor -typedef enum { - MOUSE_CURSOR_DEFAULT = 0, // Default pointer shape - MOUSE_CURSOR_ARROW = 1, // Arrow shape - MOUSE_CURSOR_IBEAM = 2, // Text writing cursor shape - MOUSE_CURSOR_CROSSHAIR = 3, // Cross shape - MOUSE_CURSOR_POINTING_HAND = 4, // Pointing hand cursor - MOUSE_CURSOR_RESIZE_EW = 5, // Horizontal resize/move arrow shape - MOUSE_CURSOR_RESIZE_NS = 6, // Vertical resize/move arrow shape - MOUSE_CURSOR_RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape - MOUSE_CURSOR_RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape - MOUSE_CURSOR_RESIZE_ALL = 9, // The omnidirectional resize/move cursor shape - MOUSE_CURSOR_NOT_ALLOWED = 10 // The operation-not-allowed shape -} MouseCursor; - -// Gamepad buttons -typedef enum { - GAMEPAD_BUTTON_UNKNOWN = 0, // Unknown button, just for error checking - GAMEPAD_BUTTON_LEFT_FACE_UP, // Gamepad left DPAD up button - GAMEPAD_BUTTON_LEFT_FACE_RIGHT, // Gamepad left DPAD right button - GAMEPAD_BUTTON_LEFT_FACE_DOWN, // Gamepad left DPAD down button - GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Gamepad left DPAD left button - GAMEPAD_BUTTON_RIGHT_FACE_UP, // Gamepad right button up (i.e. PS3: Triangle, Xbox: Y) - GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, // Gamepad right button right (i.e. PS3: Square, Xbox: X) - GAMEPAD_BUTTON_RIGHT_FACE_DOWN, // Gamepad right button down (i.e. PS3: Cross, Xbox: A) - GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // Gamepad right button left (i.e. PS3: Circle, Xbox: B) - GAMEPAD_BUTTON_LEFT_TRIGGER_1, // Gamepad top/back trigger left (first), it could be a trailing button - GAMEPAD_BUTTON_LEFT_TRIGGER_2, // Gamepad top/back trigger left (second), it could be a trailing button - GAMEPAD_BUTTON_RIGHT_TRIGGER_1, // Gamepad top/back trigger right (one), it could be a trailing button - GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // Gamepad top/back trigger right (second), it could be a trailing button - GAMEPAD_BUTTON_MIDDLE_LEFT, // Gamepad center buttons, left one (i.e. PS3: Select) - GAMEPAD_BUTTON_MIDDLE, // Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX) - GAMEPAD_BUTTON_MIDDLE_RIGHT, // Gamepad center buttons, right one (i.e. PS3: Start) - GAMEPAD_BUTTON_LEFT_THUMB, // Gamepad joystick pressed button left - GAMEPAD_BUTTON_RIGHT_THUMB // Gamepad joystick pressed button right -} GamepadButton; - -// Gamepad axis -typedef enum { - GAMEPAD_AXIS_LEFT_X = 0, // Gamepad left stick X axis - GAMEPAD_AXIS_LEFT_Y = 1, // Gamepad left stick Y axis - GAMEPAD_AXIS_RIGHT_X = 2, // Gamepad right stick X axis - GAMEPAD_AXIS_RIGHT_Y = 3, // Gamepad right stick Y axis - GAMEPAD_AXIS_LEFT_TRIGGER = 4, // Gamepad back trigger left, pressure level: [1..-1] - GAMEPAD_AXIS_RIGHT_TRIGGER = 5 // Gamepad back trigger right, pressure level: [1..-1] -} GamepadAxis; - -// Material map index -typedef enum { - MATERIAL_MAP_ALBEDO = 0, // Albedo material (same as: MATERIAL_MAP_DIFFUSE) - MATERIAL_MAP_METALNESS, // Metalness material (same as: MATERIAL_MAP_SPECULAR) - MATERIAL_MAP_NORMAL, // Normal material - MATERIAL_MAP_ROUGHNESS, // Roughness material - MATERIAL_MAP_OCCLUSION, // Ambient occlusion material - MATERIAL_MAP_EMISSION, // Emission material - MATERIAL_MAP_HEIGHT, // Heightmap material - MATERIAL_MAP_CUBEMAP, // Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP) - MATERIAL_MAP_IRRADIANCE, // Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP) - MATERIAL_MAP_PREFILTER, // Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP) - MATERIAL_MAP_BRDF // Brdf material -} MaterialMapIndex; - -#define MATERIAL_MAP_DIFFUSE MATERIAL_MAP_ALBEDO -#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS - -// Shader location index -typedef enum { - SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position - SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 - SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 - SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal - SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent - SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color - SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection - SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) - SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection - SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) - SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal - SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view - SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color - SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color - SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color - SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: SHADER_LOC_MAP_DIFFUSE) - SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: SHADER_LOC_MAP_SPECULAR) - SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal - SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness - SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion - SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission - SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height - SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap - SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance - SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter - SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf -} ShaderLocationIndex; - -#define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO -#define SHADER_LOC_MAP_SPECULAR SHADER_LOC_MAP_METALNESS - -// Shader uniform data type -typedef enum { - SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float - SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) - SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) - SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float) - SHADER_UNIFORM_INT, // Shader uniform type: int - SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) - SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) - SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) - SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d -} ShaderUniformDataType; - -// Shader attribute data types -typedef enum { - SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float - SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float) - SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float) - SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) -} ShaderAttributeDataType; - -// Pixel formats -// NOTE: Support depends on OpenGL version and platform -typedef enum { - PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) - PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) - PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp - PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp - PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) - PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) - PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp - PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) - PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) - PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) - PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) - PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) - PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp - PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp - PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp - PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp - PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp - PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp - PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp - PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp - PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp -} PixelFormat; - -// Texture parameters: filter mode -// NOTE 1: Filtering considers mipmaps if available in the texture -// NOTE 2: Filter is accordingly set for minification and magnification -typedef enum { - TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation - TEXTURE_FILTER_BILINEAR, // Linear filtering - TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) - TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x - TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x - TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x -} TextureFilter; - -// Texture parameters: wrap mode -typedef enum { - TEXTURE_WRAP_REPEAT = 0, // Repeats texture in tiled mode - TEXTURE_WRAP_CLAMP, // Clamps texture to edge pixel in tiled mode - TEXTURE_WRAP_MIRROR_REPEAT, // Mirrors and repeats the texture in tiled mode - TEXTURE_WRAP_MIRROR_CLAMP // Mirrors and clamps to border the texture in tiled mode -} TextureWrap; - -// Cubemap layouts -typedef enum { - CUBEMAP_LAYOUT_AUTO_DETECT = 0, // Automatically detect layout type - CUBEMAP_LAYOUT_LINE_VERTICAL, // Layout is defined by a vertical line with faces - CUBEMAP_LAYOUT_LINE_HORIZONTAL, // Layout is defined by a horizontal line with faces - CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces - CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces - CUBEMAP_LAYOUT_PANORAMA // Layout is defined by a panorama image (equirrectangular map) -} CubemapLayout; - -// Font type, defines generation method -typedef enum { - FONT_DEFAULT = 0, // Default font generation, anti-aliased - FONT_BITMAP, // Bitmap font generation, no anti-aliasing - FONT_SDF // SDF font generation, requires external shader -} FontType; - -// Color blending modes (pre-defined) -typedef enum { - BLEND_ALPHA = 0, // Blend textures considering alpha (default) - BLEND_ADDITIVE, // Blend textures adding colors - BLEND_MULTIPLIED, // Blend textures multiplying colors - BLEND_ADD_COLORS, // Blend textures adding colors (alternative) - BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) - BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha - BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) - BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) -} BlendMode; - -// Gesture -// NOTE: Provided as bit-wise flags to enable only desired gestures -typedef enum { - GESTURE_NONE = 0, // No gesture - GESTURE_TAP = 1, // Tap gesture - GESTURE_DOUBLETAP = 2, // Double tap gesture - GESTURE_HOLD = 4, // Hold gesture - GESTURE_DRAG = 8, // Drag gesture - GESTURE_SWIPE_RIGHT = 16, // Swipe right gesture - GESTURE_SWIPE_LEFT = 32, // Swipe left gesture - GESTURE_SWIPE_UP = 64, // Swipe up gesture - GESTURE_SWIPE_DOWN = 128, // Swipe down gesture - GESTURE_PINCH_IN = 256, // Pinch in gesture - GESTURE_PINCH_OUT = 512 // Pinch out gesture -} Gesture; - -// Camera system modes -typedef enum { - CAMERA_CUSTOM = 0, // Custom camera - CAMERA_FREE, // Free camera - CAMERA_ORBITAL, // Orbital camera - CAMERA_FIRST_PERSON, // First person camera - CAMERA_THIRD_PERSON // Third person camera -} CameraMode; - -// Camera projection -typedef enum { - CAMERA_PERSPECTIVE = 0, // Perspective projection - CAMERA_ORTHOGRAPHIC // Orthographic projection -} CameraProjection; - -// N-patch layout -typedef enum { - NPATCH_NINE_PATCH = 0, // Npatch layout: 3x3 tiles - NPATCH_THREE_PATCH_VERTICAL, // Npatch layout: 1x3 tiles - NPATCH_THREE_PATCH_HORIZONTAL // Npatch layout: 3x1 tiles -} NPatchLayout; - -// Callbacks to hook some internal functions -// WARNING: These callbacks are intended for advance users -typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages -typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data -typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data -typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data -typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data - -//------------------------------------------------------------------------------------ -// Global Variables Definition -//------------------------------------------------------------------------------------ -// It's lonely here... - -//------------------------------------------------------------------------------------ -// Window and Graphics Device Functions (Module: core) -//------------------------------------------------------------------------------------ - -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - -// Window-related functions -RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context -RLAPI bool WindowShouldClose(void); // Check if KEY_ESCAPE pressed or Close icon pressed -RLAPI void CloseWindow(void); // Close window and unload OpenGL context -RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully -RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen -RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP) -RLAPI bool IsWindowMinimized(void); // Check if window is currently minimized (only PLATFORM_DESKTOP) -RLAPI bool IsWindowMaximized(void); // Check if window is currently maximized (only PLATFORM_DESKTOP) -RLAPI bool IsWindowFocused(void); // Check if window is currently focused (only PLATFORM_DESKTOP) -RLAPI bool IsWindowResized(void); // Check if window has been resized last frame -RLAPI bool IsWindowState(unsigned int flag); // Check if one specific window flag is enabled -RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP) -RLAPI void ClearWindowState(unsigned int flags); // Clear window configuration state flags -RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) -RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) -RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) -RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) -RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) -RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) -RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP) -RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) -RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) -RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) -RLAPI void SetWindowSize(int width, int height); // Set window dimensions -RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) -RLAPI void *GetWindowHandle(void); // Get native window handle -RLAPI int GetScreenWidth(void); // Get current screen width -RLAPI int GetScreenHeight(void); // Get current screen height -RLAPI int GetRenderWidth(void); // Get current render width (it considers HiDPI) -RLAPI int GetRenderHeight(void); // Get current render height (it considers HiDPI) -RLAPI int GetMonitorCount(void); // Get number of connected monitors -RLAPI int GetCurrentMonitor(void); // Get current connected monitor -RLAPI Vector2 GetMonitorPosition(int monitor); // Get specified monitor position -RLAPI int GetMonitorWidth(int monitor); // Get specified monitor width (current video mode used by monitor) -RLAPI int GetMonitorHeight(int monitor); // Get specified monitor height (current video mode used by monitor) -RLAPI int GetMonitorPhysicalWidth(int monitor); // Get specified monitor physical width in millimetres -RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specified monitor physical height in millimetres -RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate -RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor -RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor -RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor -RLAPI void SetClipboardText(const char *text); // Set clipboard text content -RLAPI const char *GetClipboardText(void); // Get clipboard text content -RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling -RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling - -// Custom frame control functions -// NOTE: Those functions are intended for advance users that want full control over the frame processing -// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() -// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL -RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) -RLAPI void PollInputEvents(void); // Register all input events -RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) - -// Cursor-related functions -RLAPI void ShowCursor(void); // Shows cursor -RLAPI void HideCursor(void); // Hides cursor -RLAPI bool IsCursorHidden(void); // Check if cursor is not visible -RLAPI void EnableCursor(void); // Enables cursor (unlock cursor) -RLAPI void DisableCursor(void); // Disables cursor (lock cursor) -RLAPI bool IsCursorOnScreen(void); // Check if cursor is on the screen - -// Drawing-related functions -RLAPI void ClearBackground(Color color); // Set background color (framebuffer clear color) -RLAPI void BeginDrawing(void); // Setup canvas (framebuffer) to start drawing -RLAPI void EndDrawing(void); // End canvas drawing and swap buffers (double buffering) -RLAPI void BeginMode2D(Camera2D camera); // Begin 2D mode with custom camera (2D) -RLAPI void EndMode2D(void); // Ends 2D mode with custom camera -RLAPI void BeginMode3D(Camera3D camera); // Begin 3D mode with custom camera (3D) -RLAPI void EndMode3D(void); // Ends 3D mode and returns to default 2D orthographic mode -RLAPI void BeginTextureMode(RenderTexture2D target); // Begin drawing to render texture -RLAPI void EndTextureMode(void); // Ends drawing to render texture -RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing -RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader) -RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied, subtract, custom) -RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending) -RLAPI void BeginScissorMode(int x, int y, int width, int height); // Begin scissor mode (define screen area for following drawing) -RLAPI void EndScissorMode(void); // End scissor mode -RLAPI void BeginVrStereoMode(VrStereoConfig config); // Begin stereo rendering (requires VR simulator) -RLAPI void EndVrStereoMode(void); // End stereo rendering (requires VR simulator) - -// VR stereo config functions for VR simulator -RLAPI VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device); // Load VR stereo config for VR simulator device parameters -RLAPI void UnloadVrStereoConfig(VrStereoConfig config); // Unload VR stereo config - -// Shader management functions -// NOTE: Shader functionality is not available on OpenGL 1.1 -RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations -RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations -RLAPI bool IsShaderReady(Shader shader); // Check if a shader is ready -RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location -RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName); // Get shader attribute location -RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value -RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector -RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4) -RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value for texture (sampler2d) -RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) - -// Screen-space-related functions -RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Get a ray trace from mouse position -RLAPI Matrix GetCameraMatrix(Camera camera); // Get camera transform matrix (view matrix) -RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix -RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Get the screen space position for a 3d world space position -RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position -RLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position -RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the screen space position for a 2d camera world space position - -// Timing-related functions -RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) -RLAPI int GetFPS(void); // Get current FPS -RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) -RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() - -// Misc. functions -RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) -RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator -RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) -RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) - -RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) -RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level -RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator -RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator -RLAPI void MemFree(void *ptr); // Internal memory free - -RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) - -// Set custom callbacks -// WARNING: Callbacks setup is intended for advance users -RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log -RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader -RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver -RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader -RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver - -// Files management functions -RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) -RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() -RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write), returns true on success -RLAPI bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success -RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string -RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() -RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success -RLAPI bool FileExists(const char *fileName); // Check if file exists -RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists -RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav) -RLAPI int GetFileLength(const char *fileName); // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) -RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png') -RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string -RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) -RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string) -RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string) -RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) -RLAPI const char *GetApplicationDirectory(void); // Get the directory if the running application (uses static string) -RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success -RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory -RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths -RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan -RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths -RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window -RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths -RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths -RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) - -// Compression/Encoding functionality -RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree() -RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree() -RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() -RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() - -//------------------------------------------------------------------------------------ -// Input Handling Functions (Module: core) -//------------------------------------------------------------------------------------ - -// Input-related functions: keyboard -RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once -RLAPI bool IsKeyDown(int key); // Check if a key is being pressed -RLAPI bool IsKeyReleased(int key); // Check if a key has been released once -RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed -RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) -RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty -RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty - -// Input-related functions: gamepads -RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available -RLAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name id -RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once -RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed -RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once -RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed -RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed -RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad -RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis -RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) - -// Input-related functions: mouse -RLAPI bool IsMouseButtonPressed(int button); // Check if a mouse button has been pressed once -RLAPI bool IsMouseButtonDown(int button); // Check if a mouse button is being pressed -RLAPI bool IsMouseButtonReleased(int button); // Check if a mouse button has been released once -RLAPI bool IsMouseButtonUp(int button); // Check if a mouse button is NOT being pressed -RLAPI int GetMouseX(void); // Get mouse position X -RLAPI int GetMouseY(void); // Get mouse position Y -RLAPI Vector2 GetMousePosition(void); // Get mouse position XY -RLAPI Vector2 GetMouseDelta(void); // Get mouse delta between frames -RLAPI void SetMousePosition(int x, int y); // Set mouse position XY -RLAPI void SetMouseOffset(int offsetX, int offsetY); // Set mouse offset -RLAPI void SetMouseScale(float scaleX, float scaleY); // Set mouse scaling -RLAPI float GetMouseWheelMove(void); // Get mouse wheel movement for X or Y, whichever is larger -RLAPI Vector2 GetMouseWheelMoveV(void); // Get mouse wheel movement for both X and Y -RLAPI void SetMouseCursor(int cursor); // Set mouse cursor - -// Input-related functions: touch -RLAPI int GetTouchX(void); // Get touch position X for touch point 0 (relative to screen size) -RLAPI int GetTouchY(void); // Get touch position Y for touch point 0 (relative to screen size) -RLAPI Vector2 GetTouchPosition(int index); // Get touch position XY for a touch point index (relative to screen size) -RLAPI int GetTouchPointId(int index); // Get touch point identifier for given index -RLAPI int GetTouchPointCount(void); // Get number of touch points - -//------------------------------------------------------------------------------------ -// Gestures and Touch Handling Functions (Module: rgestures) -//------------------------------------------------------------------------------------ -RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected -RLAPI int GetGestureDetected(void); // Get latest detected gesture -RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds -RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector -RLAPI float GetGestureDragAngle(void); // Get gesture drag angle -RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta -RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle - -//------------------------------------------------------------------------------------ -// Camera System Functions (Module: rcamera) -//------------------------------------------------------------------------------------ -RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode -RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom); // Update camera movement/rotation - -//------------------------------------------------------------------------------------ -// Basic Shapes Drawing Functions (Module: shapes) -//------------------------------------------------------------------------------------ -// Set texture and rectangle to be used on shapes drawing -// NOTE: It can be useful when using basic shapes and one single font, -// defining a font char white rectangle would allow drawing everything in a single draw call -RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing - -// Basic shapes drawing functions -RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel -RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version) -RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line -RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (Vector version) -RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line defining thickness -RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out -RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point -RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points -RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence -RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle -RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle -RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline -RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle -RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) -RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline -RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse -RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline -RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring -RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline -RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle -RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) -RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle -RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters -RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle -RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle -RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors -RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline -RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); // Draw rectangle outline with extended parameters -RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges -RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline -RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!) -RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline (vertex in counter-clockwise order!) -RLAPI void DrawTriangleFan(Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points (first vertex is the center) -RLAPI void DrawTriangleStrip(Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points -RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) -RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a polygon outline of n sides -RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters - -// Basic shapes collision detection functions -RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles -RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles -RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle -RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle -RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle -RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle -RLAPI bool CheckCollisionPointPoly(Vector2 point, Vector2 *points, int pointCount); // Check if point is within a polygon described by array of vertices -RLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference -RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold); // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] -RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision - -//------------------------------------------------------------------------------------ -// Texture Loading and Drawing Functions (Module: textures) -//------------------------------------------------------------------------------------ - -// Image loading functions -// NOTE: These functions do not require GPU access -RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) -RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data -RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) -RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' -RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data -RLAPI Image LoadImageFromScreen(void); // Load image from screen buffer and (screenshot) -RLAPI bool IsImageReady(Image image); // Check if an image is ready -RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) -RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success -RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success - -// Image generation functions -RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color -RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient -RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient -RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient -RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked -RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise -RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise -RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells -RLAPI Image GenImageText(int width, int height, const char *text); // Generate image: grayscale image from text data - -// Image manipulation functions -RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) -RLAPI Image ImageFromImage(Image image, Rectangle rec); // Create an image from another image piece -RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) -RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) -RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format -RLAPI void ImageToPOT(Image *image, Color fill); // Convert image to POT (power-of-two) -RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle -RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value -RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); // Clear alpha channel to desired color -RLAPI void ImageAlphaMask(Image *image, Image alphaMask); // Apply alpha mask to image -RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel -RLAPI void ImageBlurGaussian(Image *image, int blurSize); // Apply Gaussian blur using a box blur approximation -RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm) -RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) -RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color -RLAPI void ImageMipmaps(Image *image); // Compute all mipmap levels for a provided image -RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) -RLAPI void ImageFlipVertical(Image *image); // Flip image vertically -RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally -RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg -RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg -RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint -RLAPI void ImageColorInvert(Image *image); // Modify image color: invert -RLAPI void ImageColorGrayscale(Image *image); // Modify image color: grayscale -RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100) -RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255) -RLAPI void ImageColorReplace(Image *image, Color color, Color replace); // Modify image color: replace color -RLAPI Color *LoadImageColors(Image image); // Load color data from image as a Color array (RGBA - 32bit) -RLAPI Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount); // Load colors palette from image as a Color array (RGBA - 32bit) -RLAPI void UnloadImageColors(Color *colors); // Unload color data loaded with LoadImageColors() -RLAPI void UnloadImagePalette(Color *colors); // Unload colors palette loaded with LoadImagePalette() -RLAPI Rectangle GetImageAlphaBorder(Image image, float threshold); // Get image alpha border rectangle -RLAPI Color GetImageColor(Image image, int x, int y); // Get image pixel color at (x, y) position - -// Image drawing functions -// NOTE: Image software-rendering functions (CPU) -RLAPI void ImageClearBackground(Image *dst, Color color); // Clear image background with given color -RLAPI void ImageDrawPixel(Image *dst, int posX, int posY, Color color); // Draw pixel within an image -RLAPI void ImageDrawPixelV(Image *dst, Vector2 position, Color color); // Draw pixel within an image (Vector version) -RLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw line within an image -RLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color); // Draw line within an image (Vector version) -RLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw a filled circle within an image -RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw a filled circle within an image (Vector version) -RLAPI void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle outline within an image -RLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color); // Draw circle outline within an image (Vector version) -RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color); // Draw rectangle within an image -RLAPI void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color); // Draw rectangle within an image (Vector version) -RLAPI void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color); // Draw rectangle within an image -RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color); // Draw rectangle lines within an image -RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source) -RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination) -RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination) - -// Texture loading functions -// NOTE: These functions require GPU access -RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) -RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data -RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout); // Load cubemap from image, multiple image cubemap layouts supported -RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) -RLAPI bool IsTextureReady(Texture2D texture); // Check if a texture is ready -RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) -RLAPI bool IsRenderTextureReady(RenderTexture2D target); // Check if a render texture is ready -RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) -RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data -RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data - -// Texture configuration functions -RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture -RLAPI void SetTextureFilter(Texture2D texture, int filter); // Set texture scaling filter mode -RLAPI void SetTextureWrap(Texture2D texture, int wrap); // Set texture wrapping mode - -// Texture drawing functions -RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D -RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 -RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters -RLAPI void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle -RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters -RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely - -// Color/pixel related functions -RLAPI Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f -RLAPI int ColorToInt(Color color); // Get hexadecimal value for a Color -RLAPI Vector4 ColorNormalize(Color color); // Get Color normalized as float [0..1] -RLAPI Color ColorFromNormalized(Vector4 normalized); // Get Color from normalized values [0..1] -RLAPI Vector3 ColorToHSV(Color color); // Get HSV values for a Color, hue [0..360], saturation/value [0..1] -RLAPI Color ColorFromHSV(float hue, float saturation, float value); // Get a Color from HSV values, hue [0..360], saturation/value [0..1] -RLAPI Color ColorTint(Color color, Color tint); // Get color multiplied with another color -RLAPI Color ColorBrightness(Color color, float factor); // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f -RLAPI Color ColorContrast(Color color, float contrast); // Get color with contrast correction, contrast values between -1.0f and 1.0f -RLAPI Color ColorAlpha(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f -RLAPI Color ColorAlphaBlend(Color dst, Color src, Color tint); // Get src alpha-blended into dst color with tint -RLAPI Color GetColor(unsigned int hexValue); // Get Color structure from hexadecimal value -RLAPI Color GetPixelColor(void *srcPtr, int format); // Get Color from a source pixel pointer of certain format -RLAPI void SetPixelColor(void *dstPtr, Color color, int format); // Set color formatted into destination pixel pointer -RLAPI int GetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes for certain format - -//------------------------------------------------------------------------------------ -// Font Loading and Text Drawing Functions (Module: text) -//------------------------------------------------------------------------------------ - -// Font loading/unloading functions -RLAPI Font GetFontDefault(void); // Get the default Font -RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set -RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) -RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' -RLAPI bool IsFontReady(Font font); // Check if a font is ready -RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type); // Load font data for further use -RLAPI Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **recs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info -RLAPI void UnloadFontData(GlyphInfo *chars, int glyphCount); // Unload font chars info data (RAM) -RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) -RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success - -// Text drawing functions -RLAPI void DrawFPS(int posX, int posY); // Draw current FPS -RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) -RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters -RLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation) -RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint) -RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) - -// Text font info functions -RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font -RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font -RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found -RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found -RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found - -// Text codepoints management functions (unicode characters) -RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array -RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array -RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter -RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory -RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string -RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) - -// Text strings management functions (no UTF-8 strings, only byte chars) -// NOTE: Some strings allocate memory internally for returned strings, just be careful! -RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied -RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal -RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending -RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf() style) -RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string -RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!) -RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!) -RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter -RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings -RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! -RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string -RLAPI const char *TextToUpper(const char *text); // Get upper case version of provided string -RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string -RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string -RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) - -//------------------------------------------------------------------------------------ -// Basic 3d Shapes Drawing Functions (Module: models) -//------------------------------------------------------------------------------------ - -// Basic geometric 3D shapes drawing functions -RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space -RLAPI void DrawPoint3D(Vector3 position, Color color); // Draw a point in 3D space, actually a small line -RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space -RLAPI void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!) -RLAPI void DrawTriangleStrip3D(Vector3 *points, int pointCount, Color color); // Draw a triangle strip defined by points -RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color); // Draw cube -RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) -RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); // Draw cube wires -RLAPI void DrawCubeWiresV(Vector3 position, Vector3 size, Color color); // Draw cube wires (Vector version) -RLAPI void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere -RLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters -RLAPI void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires -RLAPI void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone -RLAPI void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder with base at startPos and top at endPos -RLAPI void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires -RLAPI void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder wires with base at startPos and top at endPos -RLAPI void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw a capsule with the center of its sphere caps at startPos and endPos -RLAPI void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw capsule wireframe with the center of its sphere caps at startPos and endPos -RLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ -RLAPI void DrawRay(Ray ray, Color color); // Draw a ray line -RLAPI void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) - -//------------------------------------------------------------------------------------ -// Model 3d Loading and Drawing Functions (Module: models) -//------------------------------------------------------------------------------------ - -// Model management functions -RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) -RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh (default material) -RLAPI bool IsModelReady(Model model); // Check if a model is ready -RLAPI void UnloadModel(Model model); // Unload model (including meshes) from memory (RAM and/or VRAM) -RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes) - -// Model drawing functions -RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) -RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters -RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) -RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters -RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) -RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint); // Draw a billboard texture -RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source -RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation - -// Mesh management functions -RLAPI void UploadMesh(Mesh *mesh, bool dynamic); // Upload mesh vertex data in GPU and provide VAO/VBO ids -RLAPI void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset); // Update mesh vertex data in GPU for a specific buffer index -RLAPI void UnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU -RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform -RLAPI void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms -RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success -RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits -RLAPI void GenMeshTangents(Mesh *mesh); // Compute mesh tangents - -// Mesh generation functions -RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh -RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ); // Generate plane mesh (with subdivisions) -RLAPI Mesh GenMeshCube(float width, float height, float length); // Generate cuboid mesh -RLAPI Mesh GenMeshSphere(float radius, int rings, int slices); // Generate sphere mesh (standard sphere) -RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices); // Generate half-sphere mesh (no bottom cap) -RLAPI Mesh GenMeshCylinder(float radius, float height, int slices); // Generate cylinder mesh -RLAPI Mesh GenMeshCone(float radius, float height, int slices); // Generate cone/pyramid mesh -RLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides); // Generate torus mesh -RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); // Generate trefoil knot mesh -RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data -RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data - -// Material loading/unloading functions -RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file -RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -RLAPI bool IsMaterialReady(Material material); // Check if a material is ready -RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) -RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture); // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) -RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh - -// Model animations loading/unloading functions -RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount); // Load model animations from file -RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose -RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data -RLAPI void UnloadModelAnimations(ModelAnimation *animations, unsigned int count); // Unload animation array data -RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match - -// Collision detection functions -RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres -RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Check collision between two bounding boxes -RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere -RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere -RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box -RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh -RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle -RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad - -//------------------------------------------------------------------------------------ -// Audio Loading and Playing Functions (Module: audio) -//------------------------------------------------------------------------------------ -typedef void (*AudioCallback)(void *bufferData, unsigned int frames); - -// Audio device management functions -RLAPI void InitAudioDevice(void); // Initialize audio device and context -RLAPI void CloseAudioDevice(void); // Close the audio device and context -RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully -RLAPI void SetMasterVolume(float volume); // Set master volume (listener) - -// Wave/Sound loading/unloading functions -RLAPI Wave LoadWave(const char *fileName); // Load wave data from file -RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav' -RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready -RLAPI Sound LoadSound(const char *fileName); // Load sound from file -RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data -RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready -RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data -RLAPI void UnloadWave(Wave wave); // Unload wave data -RLAPI void UnloadSound(Sound sound); // Unload sound -RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success -RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success - -// Wave/Sound management functions -RLAPI void PlaySound(Sound sound); // Play a sound -RLAPI void StopSound(Sound sound); // Stop playing a sound -RLAPI void PauseSound(Sound sound); // Pause a sound -RLAPI void ResumeSound(Sound sound); // Resume a paused sound -RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing -RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) -RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) -RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (0.5 is center) -RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave -RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range -RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format -RLAPI float *LoadWaveSamples(Wave wave); // Load samples data from wave as a 32bit float data array -RLAPI void UnloadWaveSamples(float *samples); // Unload samples data loaded with LoadWaveSamples() - -// Music management functions -RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file -RLAPI Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize); // Load music stream from data -RLAPI bool IsMusicReady(Music music); // Checks if a music stream is ready -RLAPI void UnloadMusicStream(Music music); // Unload music stream -RLAPI void PlayMusicStream(Music music); // Start music playing -RLAPI bool IsMusicStreamPlaying(Music music); // Check if music is playing -RLAPI void UpdateMusicStream(Music music); // Updates buffers for music streaming -RLAPI void StopMusicStream(Music music); // Stop music playing -RLAPI void PauseMusicStream(Music music); // Pause music playing -RLAPI void ResumeMusicStream(Music music); // Resume playing paused music -RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds) -RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) -RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) -RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (0.5 is center) -RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds) -RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) - -// AudioStream management functions -RLAPI AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Load audio stream (to stream raw audio pcm data) -RLAPI bool IsAudioStreamReady(AudioStream stream); // Checks if an audio stream is ready -RLAPI void UnloadAudioStream(AudioStream stream); // Unload audio stream and free memory -RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int frameCount); // Update audio stream buffers with data -RLAPI bool IsAudioStreamProcessed(AudioStream stream); // Check if any audio stream buffers requires refill -RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream -RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream -RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream -RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing -RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream -RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) -RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) -RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered) -RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams -RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data - -RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream -RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream - -RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline -RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline - -#if defined(__cplusplus) -} -#endif - -#endif // RAYLIB_H +/********************************************************************************************** +* +* raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* +* FEATURES: +* - NO external dependencies, all required libraries included with raylib +* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, +* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5. +* - Written in plain C code (C99) in PascalCase/camelCase notation +* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3 or ES2 - choose at compile) +* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] +* - Multiple Fonts formats supported (TTF, XNA fonts, AngelCode fonts) +* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) +* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! +* - Flexible Materials system, supporting classic maps and PBR maps +* - Animated 3D models supported (skeletal bones animation) (IQM) +* - Shaders support, including Model shaders and Postprocessing shaders +* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] +* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) +* - VR stereo rendering with configurable HMD device parameters +* - Bindings to multiple programming languages available! +* +* NOTES: +* - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text] +* - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2) +* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2) +* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2) +* +* DEPENDENCIES (included): +* [rcore] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input (PLATFORM_DESKTOP) +* [rlgl] glad (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (PLATFORM_DESKTOP) +* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management +* +* OPTIONAL DEPENDENCIES (included): +* [rcore] msf_gif (Miles Fogle) for GIF recording +* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm +* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm +* [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) +* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) +* [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms +* [rtext] stb_truetype (Sean Barret) for ttf fonts loading +* [rtext] stb_rect_pack (Sean Barret) for rectangles packing +* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation +* [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) +* [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF) +* [rmodels] Model3D (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d) +* [raudio] dr_wav (David Reid) for WAV audio file loading +* [raudio] dr_flac (David Reid) for FLAC audio file loading +* [raudio] dr_mp3 (David Reid) for MP3 audio file loading +* [raudio] stb_vorbis (Sean Barret) for OGG audio loading +* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading +* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading +* +* +* LICENSE: zlib/libpng +* +* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software: +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RAYLIB_H +#define RAYLIB_H + +#include // Required for: va_list - Only used by TraceLogCallback + +#define RAYLIB_VERSION_MAJOR 4 +#define RAYLIB_VERSION_MINOR 5 +#define RAYLIB_VERSION_PATCH 0 +#define RAYLIB_VERSION "4.5" + +// Function specifiers in case library is build/used as a shared library (Windows) +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +#if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #if defined(__TINYC__) + #define __declspec(x) __attribute__((x)) + #endif + #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #endif +#endif + +#ifndef RLAPI + #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Some basic Defines +//---------------------------------------------------------------------------------- +#ifndef PI + #define PI 3.14159265358979323846f +#endif +#ifndef DEG2RAD + #define DEG2RAD (PI/180.0f) +#endif +#ifndef RAD2DEG + #define RAD2DEG (180.0f/PI) +#endif + +// Allow custom memory allocators +// NOTE: Require recompiling raylib sources +#ifndef RL_MALLOC + #define RL_MALLOC(sz) malloc(sz) +#endif +#ifndef RL_CALLOC + #define RL_CALLOC(n,sz) calloc(n,sz) +#endif +#ifndef RL_REALLOC + #define RL_REALLOC(ptr,sz) realloc(ptr,sz) +#endif +#ifndef RL_FREE + #define RL_FREE(ptr) free(ptr) +#endif + +// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) +// Plain structures in C++ (without constructors) can be initialized with { } +#if defined(__cplusplus) + #define CLITERAL(type) type +#else + #define CLITERAL(type) (type) +#endif + +// NOTE: We set some defines with some data types declared by raylib +// Other modules (raymath, rlgl) also require some of those types, so, +// to be able to use those other modules as standalone (not depending on raylib) +// this defines are very useful for internal check and avoid type (re)definitions +#define RL_COLOR_TYPE +#define RL_RECTANGLE_TYPE +#define RL_VECTOR2_TYPE +#define RL_VECTOR3_TYPE +#define RL_VECTOR4_TYPE +#define RL_QUATERNION_TYPE +#define RL_MATRIX_TYPE + +// Some Basic Colors +// NOTE: Custom raylib color palette for amazing visuals on WHITE background +#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray +#define GRAY CLITERAL(Color){ 130, 130, 130, 255 } // Gray +#define DARKGRAY CLITERAL(Color){ 80, 80, 80, 255 } // Dark Gray +#define YELLOW CLITERAL(Color){ 253, 249, 0, 255 } // Yellow +#define GOLD CLITERAL(Color){ 255, 203, 0, 255 } // Gold +#define ORANGE CLITERAL(Color){ 255, 161, 0, 255 } // Orange +#define PINK CLITERAL(Color){ 255, 109, 194, 255 } // Pink +#define RED CLITERAL(Color){ 230, 41, 55, 255 } // Red +#define MAROON CLITERAL(Color){ 190, 33, 55, 255 } // Maroon +#define GREEN CLITERAL(Color){ 0, 228, 48, 255 } // Green +#define LIME CLITERAL(Color){ 0, 158, 47, 255 } // Lime +#define DARKGREEN CLITERAL(Color){ 0, 117, 44, 255 } // Dark Green +#define SKYBLUE CLITERAL(Color){ 102, 191, 255, 255 } // Sky Blue +#define BLUE CLITERAL(Color){ 0, 121, 241, 255 } // Blue +#define DARKBLUE CLITERAL(Color){ 0, 82, 172, 255 } // Dark Blue +#define PURPLE CLITERAL(Color){ 200, 122, 255, 255 } // Purple +#define VIOLET CLITERAL(Color){ 135, 60, 190, 255 } // Violet +#define DARKPURPLE CLITERAL(Color){ 112, 31, 126, 255 } // Dark Purple +#define BEIGE CLITERAL(Color){ 211, 176, 131, 255 } // Beige +#define BROWN CLITERAL(Color){ 127, 106, 79, 255 } // Brown +#define DARKBROWN CLITERAL(Color){ 76, 63, 47, 255 } // Dark Brown + +#define WHITE CLITERAL(Color){ 255, 255, 255, 255 } // White +#define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black +#define BLANK CLITERAL(Color){ 0, 0, 0, 0 } // Blank (Transparent) +#define MAGENTA CLITERAL(Color){ 255, 0, 255, 255 } // Magenta +#define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 } // My own White (raylib logo) + +//---------------------------------------------------------------------------------- +// Structures Definition +//---------------------------------------------------------------------------------- +// Boolean type +#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum bool { false = 0, true = !false } bool; + #define RL_BOOL_TYPE +#endif + +// Vector2, 2 components +typedef struct Vector2 { + float x; // Vector x component + float y; // Vector y component +} Vector2; + +// Vector3, 3 components +typedef struct Vector3 { + float x; // Vector x component + float y; // Vector y component + float z; // Vector z component +} Vector3; + +// Vector4, 4 components +typedef struct Vector4 { + float x; // Vector x component + float y; // Vector y component + float z; // Vector z component + float w; // Vector w component +} Vector4; + +// Quaternion, 4 components (Vector4 alias) +typedef Vector4 Quaternion; + +// Matrix, 4x4 components, column major, OpenGL style, right-handed +typedef struct Matrix { + float m0, m4, m8, m12; // Matrix first row (4 components) + float m1, m5, m9, m13; // Matrix second row (4 components) + float m2, m6, m10, m14; // Matrix third row (4 components) + float m3, m7, m11, m15; // Matrix fourth row (4 components) +} Matrix; + +// Color, 4 components, R8G8B8A8 (32bit) +typedef struct Color { + unsigned char r; // Color red value + unsigned char g; // Color green value + unsigned char b; // Color blue value + unsigned char a; // Color alpha value +} Color; + +// Rectangle, 4 components +typedef struct Rectangle { + float x; // Rectangle top-left corner position x + float y; // Rectangle top-left corner position y + float width; // Rectangle width + float height; // Rectangle height +} Rectangle; + +// Image, pixel data stored in CPU memory (RAM) +typedef struct Image { + void *data; // Image raw data + int width; // Image base width + int height; // Image base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) +} Image; + +// Texture, tex data stored in GPU memory (VRAM) +typedef struct Texture { + unsigned int id; // OpenGL texture id + int width; // Texture base width + int height; // Texture base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) +} Texture; + +// Texture2D, same as Texture +typedef Texture Texture2D; + +// TextureCubemap, same as Texture +typedef Texture TextureCubemap; + +// RenderTexture, fbo for texture rendering +typedef struct RenderTexture { + unsigned int id; // OpenGL framebuffer object id + Texture texture; // Color buffer attachment texture + Texture depth; // Depth buffer attachment texture +} RenderTexture; + +// RenderTexture2D, same as RenderTexture +typedef RenderTexture RenderTexture2D; + +// NPatchInfo, n-patch layout info +typedef struct NPatchInfo { + Rectangle source; // Texture source rectangle + int left; // Left border offset + int top; // Top border offset + int right; // Right border offset + int bottom; // Bottom border offset + int layout; // Layout of the n-patch: 3x3, 1x3 or 3x1 +} NPatchInfo; + +// GlyphInfo, font characters glyphs info +typedef struct GlyphInfo { + int value; // Character value (Unicode) + int offsetX; // Character offset X when drawing + int offsetY; // Character offset Y when drawing + int advanceX; // Character advance position X + Image image; // Character image data +} GlyphInfo; + +// Font, font texture and GlyphInfo array data +typedef struct Font { + int baseSize; // Base size (default chars height) + int glyphCount; // Number of glyph characters + int glyphPadding; // Padding around the glyph characters + Texture2D texture; // Texture atlas containing the glyphs + Rectangle *recs; // Rectangles in texture for the glyphs + GlyphInfo *glyphs; // Glyphs info data +} Font; + +// Camera, defines position/orientation in 3d space +typedef struct Camera3D { + Vector3 position; // Camera position + Vector3 target; // Camera target it looks-at + Vector3 up; // Camera up vector (rotation over its axis) + float fovy; // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic + int projection; // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC +} Camera3D; + +typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D + +// Camera2D, defines position/orientation in 2d space +typedef struct Camera2D { + Vector2 offset; // Camera offset (displacement from target) + Vector2 target; // Camera target (rotation and zoom origin) + float rotation; // Camera rotation in degrees + float zoom; // Camera zoom (scaling), should be 1.0f by default +} Camera2D; + +// Mesh, vertex data and vao/vbo +typedef struct Mesh { + int vertexCount; // Number of vertices stored in arrays + int triangleCount; // Number of triangles stored (indexed or not) + + // Vertex attributes data + float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + float *texcoords2; // Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5) + float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) + float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) + unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) + unsigned short *indices; // Vertex indices (in case vertex data comes indexed) + + // Animation vertex data + float *animVertices; // Animated vertex positions (after bones transformations) + float *animNormals; // Animated normals (after bones transformations) + unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) + + // OpenGL identifiers + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data) +} Mesh; + +// Shader +typedef struct Shader { + unsigned int id; // Shader program id + int *locs; // Shader locations array (RL_MAX_SHADER_LOCATIONS) +} Shader; + +// MaterialMap +typedef struct MaterialMap { + Texture2D texture; // Material map texture + Color color; // Material map color + float value; // Material map value +} MaterialMap; + +// Material, includes shader and maps +typedef struct Material { + Shader shader; // Material shader + MaterialMap *maps; // Material maps array (MAX_MATERIAL_MAPS) + float params[4]; // Material generic parameters (if required) +} Material; + +// Transform, vertex transformation data +typedef struct Transform { + Vector3 translation; // Translation + Quaternion rotation; // Rotation + Vector3 scale; // Scale +} Transform; + +// Bone, skeletal animation bone +typedef struct BoneInfo { + char name[32]; // Bone name + int parent; // Bone parent +} BoneInfo; + +// Model, meshes, materials and animation data +typedef struct Model { + Matrix transform; // Local transform matrix + + int meshCount; // Number of meshes + int materialCount; // Number of materials + Mesh *meshes; // Meshes array + Material *materials; // Materials array + int *meshMaterial; // Mesh material number + + // Animation data + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + Transform *bindPose; // Bones base transformation (pose) +} Model; + +// ModelAnimation +typedef struct ModelAnimation { + int boneCount; // Number of bones + int frameCount; // Number of animation frames + BoneInfo *bones; // Bones information (skeleton) + Transform **framePoses; // Poses array by frame +} ModelAnimation; + +// Ray, ray for raycasting +typedef struct Ray { + Vector3 position; // Ray position (origin) + Vector3 direction; // Ray direction +} Ray; + +// RayCollision, ray hit information +typedef struct RayCollision { + bool hit; // Did the ray hit something? + float distance; // Distance to the nearest hit + Vector3 point; // Point of the nearest hit + Vector3 normal; // Surface normal of hit +} RayCollision; + +// BoundingBox +typedef struct BoundingBox { + Vector3 min; // Minimum vertex box-corner + Vector3 max; // Maximum vertex box-corner +} BoundingBox; + +// Wave, audio wave data +typedef struct Wave { + unsigned int frameCount; // Total number of frames (considering channels) + unsigned int sampleRate; // Frequency (samples per second) + unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + unsigned int channels; // Number of channels (1-mono, 2-stereo, ...) + void *data; // Buffer data pointer +} Wave; + +// Opaque structs declaration +// NOTE: Actual structs are defined internally in raudio module +typedef struct rAudioBuffer rAudioBuffer; +typedef struct rAudioProcessor rAudioProcessor; + +// AudioStream, custom audio stream +typedef struct AudioStream { + rAudioBuffer *buffer; // Pointer to internal data used by the audio system + rAudioProcessor *processor; // Pointer to internal data processor, useful for audio effects + + unsigned int sampleRate; // Frequency (samples per second) + unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + unsigned int channels; // Number of channels (1-mono, 2-stereo, ...) +} AudioStream; + +// Sound +typedef struct Sound { + AudioStream stream; // Audio stream + unsigned int frameCount; // Total number of frames (considering channels) +} Sound; + +// Music, audio stream, anything longer than ~10 seconds should be streamed +typedef struct Music { + AudioStream stream; // Audio stream + unsigned int frameCount; // Total number of frames (considering channels) + bool looping; // Music looping enable + + int ctxType; // Type of music context (audio filetype) + void *ctxData; // Audio context data, depends on type +} Music; + +// VrDeviceInfo, Head-Mounted-Display device parameters +typedef struct VrDeviceInfo { + int hResolution; // Horizontal resolution in pixels + int vResolution; // Vertical resolution in pixels + float hScreenSize; // Horizontal size in meters + float vScreenSize; // Vertical size in meters + float vScreenCenter; // Screen center in meters + float eyeToScreenDistance; // Distance between eye and display in meters + float lensSeparationDistance; // Lens separation distance in meters + float interpupillaryDistance; // IPD (distance between pupils) in meters + float lensDistortionValues[4]; // Lens distortion constant parameters + float chromaAbCorrection[4]; // Chromatic aberration correction parameters +} VrDeviceInfo; + +// VrStereoConfig, VR stereo rendering configuration for simulator +typedef struct VrStereoConfig { + Matrix projection[2]; // VR projection matrices (per eye) + Matrix viewOffset[2]; // VR view offset matrices (per eye) + float leftLensCenter[2]; // VR left lens center + float rightLensCenter[2]; // VR right lens center + float leftScreenCenter[2]; // VR left screen center + float rightScreenCenter[2]; // VR right screen center + float scale[2]; // VR distortion scale + float scaleIn[2]; // VR distortion scale in +} VrStereoConfig; + +// File path list +typedef struct FilePathList { + unsigned int capacity; // Filepaths max entries + unsigned int count; // Filepaths entries count + char **paths; // Filepaths entries +} FilePathList; + +//---------------------------------------------------------------------------------- +// Enumerators Definition +//---------------------------------------------------------------------------------- +// System/Window config flags +// NOTE: Every bit registers one state (use it with bit masks) +// By default all flags are set to 0 +typedef enum { + FLAG_VSYNC_HINT = 0x00000040, // Set to try enabling V-Sync on GPU + FLAG_FULLSCREEN_MODE = 0x00000002, // Set to run program in fullscreen + FLAG_WINDOW_RESIZABLE = 0x00000004, // Set to allow resizable window + FLAG_WINDOW_UNDECORATED = 0x00000008, // Set to disable window decoration (frame and buttons) + FLAG_WINDOW_HIDDEN = 0x00000080, // Set to hide window + FLAG_WINDOW_MINIMIZED = 0x00000200, // Set to minimize window (iconify) + FLAG_WINDOW_MAXIMIZED = 0x00000400, // Set to maximize window (expanded to monitor) + FLAG_WINDOW_UNFOCUSED = 0x00000800, // Set to window non focused + FLAG_WINDOW_TOPMOST = 0x00001000, // Set to window always on top + FLAG_WINDOW_ALWAYS_RUN = 0x00000100, // Set to allow windows running while minimized + FLAG_WINDOW_TRANSPARENT = 0x00000010, // Set to allow transparent framebuffer + FLAG_WINDOW_HIGHDPI = 0x00002000, // Set to support HighDPI + FLAG_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED + FLAG_MSAA_4X_HINT = 0x00000020, // Set to try enabling MSAA 4X + FLAG_INTERLACED_HINT = 0x00010000 // Set to try enabling interlaced video format (for V3D) +} ConfigFlags; + +// Trace log level +// NOTE: Organized by priority level +typedef enum { + LOG_ALL = 0, // Display all logs + LOG_TRACE, // Trace logging, intended for internal use only + LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds + LOG_INFO, // Info logging, used for program execution info + LOG_WARNING, // Warning logging, used on recoverable failures + LOG_ERROR, // Error logging, used on unrecoverable failures + LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) + LOG_NONE // Disable logging +} TraceLogLevel; + +// Keyboard keys (US keyboard layout) +// NOTE: Use GetKeyPressed() to allow redefining +// required keys for alternative layouts +typedef enum { + KEY_NULL = 0, // Key: NULL, used for no key pressed + // Alphanumeric keys + KEY_APOSTROPHE = 39, // Key: ' + KEY_COMMA = 44, // Key: , + KEY_MINUS = 45, // Key: - + KEY_PERIOD = 46, // Key: . + KEY_SLASH = 47, // Key: / + KEY_ZERO = 48, // Key: 0 + KEY_ONE = 49, // Key: 1 + KEY_TWO = 50, // Key: 2 + KEY_THREE = 51, // Key: 3 + KEY_FOUR = 52, // Key: 4 + KEY_FIVE = 53, // Key: 5 + KEY_SIX = 54, // Key: 6 + KEY_SEVEN = 55, // Key: 7 + KEY_EIGHT = 56, // Key: 8 + KEY_NINE = 57, // Key: 9 + KEY_SEMICOLON = 59, // Key: ; + KEY_EQUAL = 61, // Key: = + KEY_A = 65, // Key: A | a + KEY_B = 66, // Key: B | b + KEY_C = 67, // Key: C | c + KEY_D = 68, // Key: D | d + KEY_E = 69, // Key: E | e + KEY_F = 70, // Key: F | f + KEY_G = 71, // Key: G | g + KEY_H = 72, // Key: H | h + KEY_I = 73, // Key: I | i + KEY_J = 74, // Key: J | j + KEY_K = 75, // Key: K | k + KEY_L = 76, // Key: L | l + KEY_M = 77, // Key: M | m + KEY_N = 78, // Key: N | n + KEY_O = 79, // Key: O | o + KEY_P = 80, // Key: P | p + KEY_Q = 81, // Key: Q | q + KEY_R = 82, // Key: R | r + KEY_S = 83, // Key: S | s + KEY_T = 84, // Key: T | t + KEY_U = 85, // Key: U | u + KEY_V = 86, // Key: V | v + KEY_W = 87, // Key: W | w + KEY_X = 88, // Key: X | x + KEY_Y = 89, // Key: Y | y + KEY_Z = 90, // Key: Z | z + KEY_LEFT_BRACKET = 91, // Key: [ + KEY_BACKSLASH = 92, // Key: '\' + KEY_RIGHT_BRACKET = 93, // Key: ] + KEY_GRAVE = 96, // Key: ` + // Function keys + KEY_SPACE = 32, // Key: Space + KEY_ESCAPE = 256, // Key: Esc + KEY_ENTER = 257, // Key: Enter + KEY_TAB = 258, // Key: Tab + KEY_BACKSPACE = 259, // Key: Backspace + KEY_INSERT = 260, // Key: Ins + KEY_DELETE = 261, // Key: Del + KEY_RIGHT = 262, // Key: Cursor right + KEY_LEFT = 263, // Key: Cursor left + KEY_DOWN = 264, // Key: Cursor down + KEY_UP = 265, // Key: Cursor up + KEY_PAGE_UP = 266, // Key: Page up + KEY_PAGE_DOWN = 267, // Key: Page down + KEY_HOME = 268, // Key: Home + KEY_END = 269, // Key: End + KEY_CAPS_LOCK = 280, // Key: Caps lock + KEY_SCROLL_LOCK = 281, // Key: Scroll down + KEY_NUM_LOCK = 282, // Key: Num lock + KEY_PRINT_SCREEN = 283, // Key: Print screen + KEY_PAUSE = 284, // Key: Pause + KEY_F1 = 290, // Key: F1 + KEY_F2 = 291, // Key: F2 + KEY_F3 = 292, // Key: F3 + KEY_F4 = 293, // Key: F4 + KEY_F5 = 294, // Key: F5 + KEY_F6 = 295, // Key: F6 + KEY_F7 = 296, // Key: F7 + KEY_F8 = 297, // Key: F8 + KEY_F9 = 298, // Key: F9 + KEY_F10 = 299, // Key: F10 + KEY_F11 = 300, // Key: F11 + KEY_F12 = 301, // Key: F12 + KEY_LEFT_SHIFT = 340, // Key: Shift left + KEY_LEFT_CONTROL = 341, // Key: Control left + KEY_LEFT_ALT = 342, // Key: Alt left + KEY_LEFT_SUPER = 343, // Key: Super left + KEY_RIGHT_SHIFT = 344, // Key: Shift right + KEY_RIGHT_CONTROL = 345, // Key: Control right + KEY_RIGHT_ALT = 346, // Key: Alt right + KEY_RIGHT_SUPER = 347, // Key: Super right + KEY_KB_MENU = 348, // Key: KB menu + // Keypad keys + KEY_KP_0 = 320, // Key: Keypad 0 + KEY_KP_1 = 321, // Key: Keypad 1 + KEY_KP_2 = 322, // Key: Keypad 2 + KEY_KP_3 = 323, // Key: Keypad 3 + KEY_KP_4 = 324, // Key: Keypad 4 + KEY_KP_5 = 325, // Key: Keypad 5 + KEY_KP_6 = 326, // Key: Keypad 6 + KEY_KP_7 = 327, // Key: Keypad 7 + KEY_KP_8 = 328, // Key: Keypad 8 + KEY_KP_9 = 329, // Key: Keypad 9 + KEY_KP_DECIMAL = 330, // Key: Keypad . + KEY_KP_DIVIDE = 331, // Key: Keypad / + KEY_KP_MULTIPLY = 332, // Key: Keypad * + KEY_KP_SUBTRACT = 333, // Key: Keypad - + KEY_KP_ADD = 334, // Key: Keypad + + KEY_KP_ENTER = 335, // Key: Keypad Enter + KEY_KP_EQUAL = 336, // Key: Keypad = + // Android key buttons + KEY_BACK = 4, // Key: Android back button + KEY_MENU = 82, // Key: Android menu button + KEY_VOLUME_UP = 24, // Key: Android volume up button + KEY_VOLUME_DOWN = 25 // Key: Android volume down button +} KeyboardKey; + +// Add backwards compatibility support for deprecated names +#define MOUSE_LEFT_BUTTON MOUSE_BUTTON_LEFT +#define MOUSE_RIGHT_BUTTON MOUSE_BUTTON_RIGHT +#define MOUSE_MIDDLE_BUTTON MOUSE_BUTTON_MIDDLE + +// Mouse buttons +typedef enum { + MOUSE_BUTTON_LEFT = 0, // Mouse button left + MOUSE_BUTTON_RIGHT = 1, // Mouse button right + MOUSE_BUTTON_MIDDLE = 2, // Mouse button middle (pressed wheel) + MOUSE_BUTTON_SIDE = 3, // Mouse button side (advanced mouse device) + MOUSE_BUTTON_EXTRA = 4, // Mouse button extra (advanced mouse device) + MOUSE_BUTTON_FORWARD = 5, // Mouse button forward (advanced mouse device) + MOUSE_BUTTON_BACK = 6, // Mouse button back (advanced mouse device) +} MouseButton; + +// Mouse cursor +typedef enum { + MOUSE_CURSOR_DEFAULT = 0, // Default pointer shape + MOUSE_CURSOR_ARROW = 1, // Arrow shape + MOUSE_CURSOR_IBEAM = 2, // Text writing cursor shape + MOUSE_CURSOR_CROSSHAIR = 3, // Cross shape + MOUSE_CURSOR_POINTING_HAND = 4, // Pointing hand cursor + MOUSE_CURSOR_RESIZE_EW = 5, // Horizontal resize/move arrow shape + MOUSE_CURSOR_RESIZE_NS = 6, // Vertical resize/move arrow shape + MOUSE_CURSOR_RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape + MOUSE_CURSOR_RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape + MOUSE_CURSOR_RESIZE_ALL = 9, // The omnidirectional resize/move cursor shape + MOUSE_CURSOR_NOT_ALLOWED = 10 // The operation-not-allowed shape +} MouseCursor; + +// Gamepad buttons +typedef enum { + GAMEPAD_BUTTON_UNKNOWN = 0, // Unknown button, just for error checking + GAMEPAD_BUTTON_LEFT_FACE_UP, // Gamepad left DPAD up button + GAMEPAD_BUTTON_LEFT_FACE_RIGHT, // Gamepad left DPAD right button + GAMEPAD_BUTTON_LEFT_FACE_DOWN, // Gamepad left DPAD down button + GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Gamepad left DPAD left button + GAMEPAD_BUTTON_RIGHT_FACE_UP, // Gamepad right button up (i.e. PS3: Triangle, Xbox: Y) + GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, // Gamepad right button right (i.e. PS3: Square, Xbox: X) + GAMEPAD_BUTTON_RIGHT_FACE_DOWN, // Gamepad right button down (i.e. PS3: Cross, Xbox: A) + GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // Gamepad right button left (i.e. PS3: Circle, Xbox: B) + GAMEPAD_BUTTON_LEFT_TRIGGER_1, // Gamepad top/back trigger left (first), it could be a trailing button + GAMEPAD_BUTTON_LEFT_TRIGGER_2, // Gamepad top/back trigger left (second), it could be a trailing button + GAMEPAD_BUTTON_RIGHT_TRIGGER_1, // Gamepad top/back trigger right (one), it could be a trailing button + GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // Gamepad top/back trigger right (second), it could be a trailing button + GAMEPAD_BUTTON_MIDDLE_LEFT, // Gamepad center buttons, left one (i.e. PS3: Select) + GAMEPAD_BUTTON_MIDDLE, // Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX) + GAMEPAD_BUTTON_MIDDLE_RIGHT, // Gamepad center buttons, right one (i.e. PS3: Start) + GAMEPAD_BUTTON_LEFT_THUMB, // Gamepad joystick pressed button left + GAMEPAD_BUTTON_RIGHT_THUMB // Gamepad joystick pressed button right +} GamepadButton; + +// Gamepad axis +typedef enum { + GAMEPAD_AXIS_LEFT_X = 0, // Gamepad left stick X axis + GAMEPAD_AXIS_LEFT_Y = 1, // Gamepad left stick Y axis + GAMEPAD_AXIS_RIGHT_X = 2, // Gamepad right stick X axis + GAMEPAD_AXIS_RIGHT_Y = 3, // Gamepad right stick Y axis + GAMEPAD_AXIS_LEFT_TRIGGER = 4, // Gamepad back trigger left, pressure level: [1..-1] + GAMEPAD_AXIS_RIGHT_TRIGGER = 5 // Gamepad back trigger right, pressure level: [1..-1] +} GamepadAxis; + +// Material map index +typedef enum { + MATERIAL_MAP_ALBEDO = 0, // Albedo material (same as: MATERIAL_MAP_DIFFUSE) + MATERIAL_MAP_METALNESS, // Metalness material (same as: MATERIAL_MAP_SPECULAR) + MATERIAL_MAP_NORMAL, // Normal material + MATERIAL_MAP_ROUGHNESS, // Roughness material + MATERIAL_MAP_OCCLUSION, // Ambient occlusion material + MATERIAL_MAP_EMISSION, // Emission material + MATERIAL_MAP_HEIGHT, // Heightmap material + MATERIAL_MAP_CUBEMAP, // Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP) + MATERIAL_MAP_IRRADIANCE, // Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP) + MATERIAL_MAP_PREFILTER, // Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP) + MATERIAL_MAP_BRDF // Brdf material +} MaterialMapIndex; + +#define MATERIAL_MAP_DIFFUSE MATERIAL_MAP_ALBEDO +#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS + +// Shader location index +typedef enum { + SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position + SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 + SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 + SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal + SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent + SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color + SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection + SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) + SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection + SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) + SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal + SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view + SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color + SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color + SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color + SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: SHADER_LOC_MAP_DIFFUSE) + SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: SHADER_LOC_MAP_SPECULAR) + SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal + SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness + SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion + SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission + SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height + SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap + SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance + SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter + SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf +} ShaderLocationIndex; + +#define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO +#define SHADER_LOC_MAP_SPECULAR SHADER_LOC_MAP_METALNESS + +// Shader uniform data type +typedef enum { + SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float + SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) + SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) + SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float) + SHADER_UNIFORM_INT, // Shader uniform type: int + SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) + SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) + SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) + SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d +} ShaderUniformDataType; + +// Shader attribute data types +typedef enum { + SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float + SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float) + SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float) + SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) +} ShaderAttributeDataType; + +// Pixel formats +// NOTE: Support depends on OpenGL version and platform +typedef enum { + PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp + PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp + PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp + PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) + PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) + PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) + PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp + PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp + PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp + PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp + PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp + PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp + PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp +} PixelFormat; + +// Texture parameters: filter mode +// NOTE 1: Filtering considers mipmaps if available in the texture +// NOTE 2: Filter is accordingly set for minification and magnification +typedef enum { + TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation + TEXTURE_FILTER_BILINEAR, // Linear filtering + TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) + TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x + TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x + TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x +} TextureFilter; + +// Texture parameters: wrap mode +typedef enum { + TEXTURE_WRAP_REPEAT = 0, // Repeats texture in tiled mode + TEXTURE_WRAP_CLAMP, // Clamps texture to edge pixel in tiled mode + TEXTURE_WRAP_MIRROR_REPEAT, // Mirrors and repeats the texture in tiled mode + TEXTURE_WRAP_MIRROR_CLAMP // Mirrors and clamps to border the texture in tiled mode +} TextureWrap; + +// Cubemap layouts +typedef enum { + CUBEMAP_LAYOUT_AUTO_DETECT = 0, // Automatically detect layout type + CUBEMAP_LAYOUT_LINE_VERTICAL, // Layout is defined by a vertical line with faces + CUBEMAP_LAYOUT_LINE_HORIZONTAL, // Layout is defined by a horizontal line with faces + CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces + CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces + CUBEMAP_LAYOUT_PANORAMA // Layout is defined by a panorama image (equirrectangular map) +} CubemapLayout; + +// Font type, defines generation method +typedef enum { + FONT_DEFAULT = 0, // Default font generation, anti-aliased + FONT_BITMAP, // Bitmap font generation, no anti-aliasing + FONT_SDF // SDF font generation, requires external shader +} FontType; + +// Color blending modes (pre-defined) +typedef enum { + BLEND_ALPHA = 0, // Blend textures considering alpha (default) + BLEND_ADDITIVE, // Blend textures adding colors + BLEND_MULTIPLIED, // Blend textures multiplying colors + BLEND_ADD_COLORS, // Blend textures adding colors (alternative) + BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) + BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha + BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) +} BlendMode; + +// Gesture +// NOTE: Provided as bit-wise flags to enable only desired gestures +typedef enum { + GESTURE_NONE = 0, // No gesture + GESTURE_TAP = 1, // Tap gesture + GESTURE_DOUBLETAP = 2, // Double tap gesture + GESTURE_HOLD = 4, // Hold gesture + GESTURE_DRAG = 8, // Drag gesture + GESTURE_SWIPE_RIGHT = 16, // Swipe right gesture + GESTURE_SWIPE_LEFT = 32, // Swipe left gesture + GESTURE_SWIPE_UP = 64, // Swipe up gesture + GESTURE_SWIPE_DOWN = 128, // Swipe down gesture + GESTURE_PINCH_IN = 256, // Pinch in gesture + GESTURE_PINCH_OUT = 512 // Pinch out gesture +} Gesture; + +// Camera system modes +typedef enum { + CAMERA_CUSTOM = 0, // Custom camera + CAMERA_FREE, // Free camera + CAMERA_ORBITAL, // Orbital camera + CAMERA_FIRST_PERSON, // First person camera + CAMERA_THIRD_PERSON // Third person camera +} CameraMode; + +// Camera projection +typedef enum { + CAMERA_PERSPECTIVE = 0, // Perspective projection + CAMERA_ORTHOGRAPHIC // Orthographic projection +} CameraProjection; + +// N-patch layout +typedef enum { + NPATCH_NINE_PATCH = 0, // Npatch layout: 3x3 tiles + NPATCH_THREE_PATCH_VERTICAL, // Npatch layout: 1x3 tiles + NPATCH_THREE_PATCH_HORIZONTAL // Npatch layout: 3x1 tiles +} NPatchLayout; + +// Callbacks to hook some internal functions +// WARNING: These callbacks are intended for advance users +typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages +typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data +typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data +typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data +typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data + +//------------------------------------------------------------------------------------ +// Global Variables Definition +//------------------------------------------------------------------------------------ +// It's lonely here... + +//------------------------------------------------------------------------------------ +// Window and Graphics Device Functions (Module: core) +//------------------------------------------------------------------------------------ + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +// Window-related functions +RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context +RLAPI bool WindowShouldClose(void); // Check if KEY_ESCAPE pressed or Close icon pressed +RLAPI void CloseWindow(void); // Close window and unload OpenGL context +RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully +RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen +RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP) +RLAPI bool IsWindowMinimized(void); // Check if window is currently minimized (only PLATFORM_DESKTOP) +RLAPI bool IsWindowMaximized(void); // Check if window is currently maximized (only PLATFORM_DESKTOP) +RLAPI bool IsWindowFocused(void); // Check if window is currently focused (only PLATFORM_DESKTOP) +RLAPI bool IsWindowResized(void); // Check if window has been resized last frame +RLAPI bool IsWindowState(unsigned int flag); // Check if one specific window flag is enabled +RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP) +RLAPI void ClearWindowState(unsigned int flags); // Clear window configuration state flags +RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) +RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) +RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) +RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) +RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) +RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) +RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP) +RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) +RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) +RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) +RLAPI void SetWindowSize(int width, int height); // Set window dimensions +RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) +RLAPI void *GetWindowHandle(void); // Get native window handle +RLAPI int GetScreenWidth(void); // Get current screen width +RLAPI int GetScreenHeight(void); // Get current screen height +RLAPI int GetRenderWidth(void); // Get current render width (it considers HiDPI) +RLAPI int GetRenderHeight(void); // Get current render height (it considers HiDPI) +RLAPI int GetMonitorCount(void); // Get number of connected monitors +RLAPI int GetCurrentMonitor(void); // Get current connected monitor +RLAPI Vector2 GetMonitorPosition(int monitor); // Get specified monitor position +RLAPI int GetMonitorWidth(int monitor); // Get specified monitor width (current video mode used by monitor) +RLAPI int GetMonitorHeight(int monitor); // Get specified monitor height (current video mode used by monitor) +RLAPI int GetMonitorPhysicalWidth(int monitor); // Get specified monitor physical width in millimetres +RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specified monitor physical height in millimetres +RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate +RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor +RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor +RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor +RLAPI void SetClipboardText(const char *text); // Set clipboard text content +RLAPI const char *GetClipboardText(void); // Get clipboard text content +RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling +RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling + +// Custom frame control functions +// NOTE: Those functions are intended for advance users that want full control over the frame processing +// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() +// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL +RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) +RLAPI void PollInputEvents(void); // Register all input events +RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) + +// Cursor-related functions +RLAPI void ShowCursor(void); // Shows cursor +RLAPI void HideCursor(void); // Hides cursor +RLAPI bool IsCursorHidden(void); // Check if cursor is not visible +RLAPI void EnableCursor(void); // Enables cursor (unlock cursor) +RLAPI void DisableCursor(void); // Disables cursor (lock cursor) +RLAPI bool IsCursorOnScreen(void); // Check if cursor is on the screen + +// Drawing-related functions +RLAPI void ClearBackground(Color color); // Set background color (framebuffer clear color) +RLAPI void BeginDrawing(void); // Setup canvas (framebuffer) to start drawing +RLAPI void EndDrawing(void); // End canvas drawing and swap buffers (double buffering) +RLAPI void BeginMode2D(Camera2D camera); // Begin 2D mode with custom camera (2D) +RLAPI void EndMode2D(void); // Ends 2D mode with custom camera +RLAPI void BeginMode3D(Camera3D camera); // Begin 3D mode with custom camera (3D) +RLAPI void EndMode3D(void); // Ends 3D mode and returns to default 2D orthographic mode +RLAPI void BeginTextureMode(RenderTexture2D target); // Begin drawing to render texture +RLAPI void EndTextureMode(void); // Ends drawing to render texture +RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing +RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader) +RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied, subtract, custom) +RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending) +RLAPI void BeginScissorMode(int x, int y, int width, int height); // Begin scissor mode (define screen area for following drawing) +RLAPI void EndScissorMode(void); // End scissor mode +RLAPI void BeginVrStereoMode(VrStereoConfig config); // Begin stereo rendering (requires VR simulator) +RLAPI void EndVrStereoMode(void); // End stereo rendering (requires VR simulator) + +// VR stereo config functions for VR simulator +RLAPI VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device); // Load VR stereo config for VR simulator device parameters +RLAPI void UnloadVrStereoConfig(VrStereoConfig config); // Unload VR stereo config + +// Shader management functions +// NOTE: Shader functionality is not available on OpenGL 1.1 +RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations +RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations +RLAPI bool IsShaderReady(Shader shader); // Check if a shader is ready +RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location +RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName); // Get shader attribute location +RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value +RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector +RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4) +RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value for texture (sampler2d) +RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) + +// Screen-space-related functions +RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Get a ray trace from mouse position +RLAPI Matrix GetCameraMatrix(Camera camera); // Get camera transform matrix (view matrix) +RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix +RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Get the screen space position for a 3d world space position +RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position +RLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position +RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the screen space position for a 2d camera world space position + +// Timing-related functions +RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) +RLAPI int GetFPS(void); // Get current FPS +RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) +RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() + +// Misc. functions +RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) +RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator +RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) +RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) + +RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) +RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level +RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator +RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator +RLAPI void MemFree(void *ptr); // Internal memory free + +RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) + +// Set custom callbacks +// WARNING: Callbacks setup is intended for advance users +RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log +RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader +RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver +RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader +RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver + +// Files management functions +RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) +RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() +RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write), returns true on success +RLAPI bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success +RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string +RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() +RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success +RLAPI bool FileExists(const char *fileName); // Check if file exists +RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists +RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav) +RLAPI int GetFileLength(const char *fileName); // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) +RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png') +RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string +RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) +RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string) +RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string) +RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) +RLAPI const char *GetApplicationDirectory(void); // Get the directory if the running application (uses static string) +RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success +RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory +RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths +RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan +RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths +RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window +RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths +RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths +RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) + +// Compression/Encoding functionality +RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree() +RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree() +RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() +RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() + +//------------------------------------------------------------------------------------ +// Input Handling Functions (Module: core) +//------------------------------------------------------------------------------------ + +// Input-related functions: keyboard +RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once +RLAPI bool IsKeyDown(int key); // Check if a key is being pressed +RLAPI bool IsKeyReleased(int key); // Check if a key has been released once +RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed +RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) +RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty +RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty + +// Input-related functions: gamepads +RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available +RLAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name id +RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once +RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed +RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once +RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed +RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed +RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad +RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis +RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) + +// Input-related functions: mouse +RLAPI bool IsMouseButtonPressed(int button); // Check if a mouse button has been pressed once +RLAPI bool IsMouseButtonDown(int button); // Check if a mouse button is being pressed +RLAPI bool IsMouseButtonReleased(int button); // Check if a mouse button has been released once +RLAPI bool IsMouseButtonUp(int button); // Check if a mouse button is NOT being pressed +RLAPI int GetMouseX(void); // Get mouse position X +RLAPI int GetMouseY(void); // Get mouse position Y +RLAPI Vector2 GetMousePosition(void); // Get mouse position XY +RLAPI Vector2 GetMouseDelta(void); // Get mouse delta between frames +RLAPI void SetMousePosition(int x, int y); // Set mouse position XY +RLAPI void SetMouseOffset(int offsetX, int offsetY); // Set mouse offset +RLAPI void SetMouseScale(float scaleX, float scaleY); // Set mouse scaling +RLAPI float GetMouseWheelMove(void); // Get mouse wheel movement for X or Y, whichever is larger +RLAPI Vector2 GetMouseWheelMoveV(void); // Get mouse wheel movement for both X and Y +RLAPI void SetMouseCursor(int cursor); // Set mouse cursor + +// Input-related functions: touch +RLAPI int GetTouchX(void); // Get touch position X for touch point 0 (relative to screen size) +RLAPI int GetTouchY(void); // Get touch position Y for touch point 0 (relative to screen size) +RLAPI Vector2 GetTouchPosition(int index); // Get touch position XY for a touch point index (relative to screen size) +RLAPI int GetTouchPointId(int index); // Get touch point identifier for given index +RLAPI int GetTouchPointCount(void); // Get number of touch points + +//------------------------------------------------------------------------------------ +// Gestures and Touch Handling Functions (Module: rgestures) +//------------------------------------------------------------------------------------ +RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags +RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected +RLAPI int GetGestureDetected(void); // Get latest detected gesture +RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds +RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector +RLAPI float GetGestureDragAngle(void); // Get gesture drag angle +RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta +RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle + +//------------------------------------------------------------------------------------ +// Camera System Functions (Module: rcamera) +//------------------------------------------------------------------------------------ +RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode +RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom); // Update camera movement/rotation + +//------------------------------------------------------------------------------------ +// Basic Shapes Drawing Functions (Module: shapes) +//------------------------------------------------------------------------------------ +// Set texture and rectangle to be used on shapes drawing +// NOTE: It can be useful when using basic shapes and one single font, +// defining a font char white rectangle would allow drawing everything in a single draw call +RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing + +// Basic shapes drawing functions +RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel +RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version) +RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line +RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (Vector version) +RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line defining thickness +RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out +RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point +RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points +RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence +RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle +RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle +RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline +RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle +RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) +RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline +RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse +RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline +RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring +RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline +RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle +RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) +RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle +RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters +RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle +RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle +RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors +RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline +RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); // Draw rectangle outline with extended parameters +RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges +RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline +RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!) +RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline (vertex in counter-clockwise order!) +RLAPI void DrawTriangleFan(Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points (first vertex is the center) +RLAPI void DrawTriangleStrip(Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points +RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) +RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a polygon outline of n sides +RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters + +// Basic shapes collision detection functions +RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles +RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles +RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle +RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle +RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle +RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle +RLAPI bool CheckCollisionPointPoly(Vector2 point, Vector2 *points, int pointCount); // Check if point is within a polygon described by array of vertices +RLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference +RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold); // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] +RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision + +//------------------------------------------------------------------------------------ +// Texture Loading and Drawing Functions (Module: textures) +//------------------------------------------------------------------------------------ + +// Image loading functions +// NOTE: These functions do not require GPU access +RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) +RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data +RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) +RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' +RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data +RLAPI Image LoadImageFromScreen(void); // Load image from screen buffer and (screenshot) +RLAPI bool IsImageReady(Image image); // Check if an image is ready +RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) +RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success +RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success + +// Image generation functions +RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color +RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient +RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient +RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient +RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked +RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise +RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise +RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells +RLAPI Image GenImageText(int width, int height, const char *text); // Generate image: grayscale image from text data + +// Image manipulation functions +RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) +RLAPI Image ImageFromImage(Image image, Rectangle rec); // Create an image from another image piece +RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) +RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) +RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format +RLAPI void ImageToPOT(Image *image, Color fill); // Convert image to POT (power-of-two) +RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle +RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value +RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); // Clear alpha channel to desired color +RLAPI void ImageAlphaMask(Image *image, Image alphaMask); // Apply alpha mask to image +RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel +RLAPI void ImageBlurGaussian(Image *image, int blurSize); // Apply Gaussian blur using a box blur approximation +RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm) +RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) +RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color +RLAPI void ImageMipmaps(Image *image); // Compute all mipmap levels for a provided image +RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) +RLAPI void ImageFlipVertical(Image *image); // Flip image vertically +RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally +RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg +RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg +RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint +RLAPI void ImageColorInvert(Image *image); // Modify image color: invert +RLAPI void ImageColorGrayscale(Image *image); // Modify image color: grayscale +RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100) +RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255) +RLAPI void ImageColorReplace(Image *image, Color color, Color replace); // Modify image color: replace color +RLAPI Color *LoadImageColors(Image image); // Load color data from image as a Color array (RGBA - 32bit) +RLAPI Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount); // Load colors palette from image as a Color array (RGBA - 32bit) +RLAPI void UnloadImageColors(Color *colors); // Unload color data loaded with LoadImageColors() +RLAPI void UnloadImagePalette(Color *colors); // Unload colors palette loaded with LoadImagePalette() +RLAPI Rectangle GetImageAlphaBorder(Image image, float threshold); // Get image alpha border rectangle +RLAPI Color GetImageColor(Image image, int x, int y); // Get image pixel color at (x, y) position + +// Image drawing functions +// NOTE: Image software-rendering functions (CPU) +RLAPI void ImageClearBackground(Image *dst, Color color); // Clear image background with given color +RLAPI void ImageDrawPixel(Image *dst, int posX, int posY, Color color); // Draw pixel within an image +RLAPI void ImageDrawPixelV(Image *dst, Vector2 position, Color color); // Draw pixel within an image (Vector version) +RLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw line within an image +RLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color); // Draw line within an image (Vector version) +RLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw a filled circle within an image +RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw a filled circle within an image (Vector version) +RLAPI void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle outline within an image +RLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color); // Draw circle outline within an image (Vector version) +RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color); // Draw rectangle within an image +RLAPI void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color); // Draw rectangle within an image (Vector version) +RLAPI void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color); // Draw rectangle within an image +RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color); // Draw rectangle lines within an image +RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source) +RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination) +RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination) + +// Texture loading functions +// NOTE: These functions require GPU access +RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) +RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data +RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout); // Load cubemap from image, multiple image cubemap layouts supported +RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) +RLAPI bool IsTextureReady(Texture2D texture); // Check if a texture is ready +RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) +RLAPI bool IsRenderTextureReady(RenderTexture2D target); // Check if a render texture is ready +RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) +RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data +RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data + +// Texture configuration functions +RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture +RLAPI void SetTextureFilter(Texture2D texture, int filter); // Set texture scaling filter mode +RLAPI void SetTextureWrap(Texture2D texture, int wrap); // Set texture wrapping mode + +// Texture drawing functions +RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D +RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 +RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters +RLAPI void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle +RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters +RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely + +// Color/pixel related functions +RLAPI Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f +RLAPI int ColorToInt(Color color); // Get hexadecimal value for a Color +RLAPI Vector4 ColorNormalize(Color color); // Get Color normalized as float [0..1] +RLAPI Color ColorFromNormalized(Vector4 normalized); // Get Color from normalized values [0..1] +RLAPI Vector3 ColorToHSV(Color color); // Get HSV values for a Color, hue [0..360], saturation/value [0..1] +RLAPI Color ColorFromHSV(float hue, float saturation, float value); // Get a Color from HSV values, hue [0..360], saturation/value [0..1] +RLAPI Color ColorTint(Color color, Color tint); // Get color multiplied with another color +RLAPI Color ColorBrightness(Color color, float factor); // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f +RLAPI Color ColorContrast(Color color, float contrast); // Get color with contrast correction, contrast values between -1.0f and 1.0f +RLAPI Color ColorAlpha(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f +RLAPI Color ColorAlphaBlend(Color dst, Color src, Color tint); // Get src alpha-blended into dst color with tint +RLAPI Color GetColor(unsigned int hexValue); // Get Color structure from hexadecimal value +RLAPI Color GetPixelColor(void *srcPtr, int format); // Get Color from a source pixel pointer of certain format +RLAPI void SetPixelColor(void *dstPtr, Color color, int format); // Set color formatted into destination pixel pointer +RLAPI int GetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes for certain format + +//------------------------------------------------------------------------------------ +// Font Loading and Text Drawing Functions (Module: text) +//------------------------------------------------------------------------------------ + +// Font loading/unloading functions +RLAPI Font GetFontDefault(void); // Get the default Font +RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) +RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set +RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) +RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' +RLAPI bool IsFontReady(Font font); // Check if a font is ready +RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type); // Load font data for further use +RLAPI Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **recs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info +RLAPI void UnloadFontData(GlyphInfo *chars, int glyphCount); // Unload font chars info data (RAM) +RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) +RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success + +// Text drawing functions +RLAPI void DrawFPS(int posX, int posY); // Draw current FPS +RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) +RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters +RLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation) +RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint) +RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) + +// Text font info functions +RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font +RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font +RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found +RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found +RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found + +// Text codepoints management functions (unicode characters) +RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array +RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array +RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter +RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory +RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string +RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) + +// Text strings management functions (no UTF-8 strings, only byte chars) +// NOTE: Some strings allocate memory internally for returned strings, just be careful! +RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied +RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal +RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending +RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf() style) +RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string +RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!) +RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!) +RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter +RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! +RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string +RLAPI const char *TextToUpper(const char *text); // Get upper case version of provided string +RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string +RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string +RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) + +//------------------------------------------------------------------------------------ +// Basic 3d Shapes Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ + +// Basic geometric 3D shapes drawing functions +RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space +RLAPI void DrawPoint3D(Vector3 position, Color color); // Draw a point in 3D space, actually a small line +RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space +RLAPI void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!) +RLAPI void DrawTriangleStrip3D(Vector3 *points, int pointCount, Color color); // Draw a triangle strip defined by points +RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color); // Draw cube +RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) +RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); // Draw cube wires +RLAPI void DrawCubeWiresV(Vector3 position, Vector3 size, Color color); // Draw cube wires (Vector version) +RLAPI void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere +RLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters +RLAPI void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires +RLAPI void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone +RLAPI void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder with base at startPos and top at endPos +RLAPI void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires +RLAPI void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder wires with base at startPos and top at endPos +RLAPI void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw a capsule with the center of its sphere caps at startPos and endPos +RLAPI void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw capsule wireframe with the center of its sphere caps at startPos and endPos +RLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ +RLAPI void DrawRay(Ray ray, Color color); // Draw a ray line +RLAPI void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) + +//------------------------------------------------------------------------------------ +// Model 3d Loading and Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ + +// Model management functions +RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) +RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh (default material) +RLAPI bool IsModelReady(Model model); // Check if a model is ready +RLAPI void UnloadModel(Model model); // Unload model (including meshes) from memory (RAM and/or VRAM) +RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes) + +// Model drawing functions +RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) +RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters +RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) +RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters +RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) +RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint); // Draw a billboard texture +RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source +RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation + +// Mesh management functions +RLAPI void UploadMesh(Mesh *mesh, bool dynamic); // Upload mesh vertex data in GPU and provide VAO/VBO ids +RLAPI void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset); // Update mesh vertex data in GPU for a specific buffer index +RLAPI void UnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU +RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform +RLAPI void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms +RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success +RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits +RLAPI void GenMeshTangents(Mesh *mesh); // Compute mesh tangents + +// Mesh generation functions +RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh +RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ); // Generate plane mesh (with subdivisions) +RLAPI Mesh GenMeshCube(float width, float height, float length); // Generate cuboid mesh +RLAPI Mesh GenMeshSphere(float radius, int rings, int slices); // Generate sphere mesh (standard sphere) +RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices); // Generate half-sphere mesh (no bottom cap) +RLAPI Mesh GenMeshCylinder(float radius, float height, int slices); // Generate cylinder mesh +RLAPI Mesh GenMeshCone(float radius, float height, int slices); // Generate cone/pyramid mesh +RLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides); // Generate torus mesh +RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); // Generate trefoil knot mesh +RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data +RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data + +// Material loading/unloading functions +RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file +RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +RLAPI bool IsMaterialReady(Material material); // Check if a material is ready +RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) +RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture); // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) +RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh + +// Model animations loading/unloading functions +RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount); // Load model animations from file +RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose +RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data +RLAPI void UnloadModelAnimations(ModelAnimation *animations, unsigned int count); // Unload animation array data +RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match + +// Collision detection functions +RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres +RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Check collision between two bounding boxes +RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere +RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere +RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box +RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh +RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle +RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad + +//------------------------------------------------------------------------------------ +// Audio Loading and Playing Functions (Module: audio) +//------------------------------------------------------------------------------------ +typedef void (*AudioCallback)(void *bufferData, unsigned int frames); + +// Audio device management functions +RLAPI void InitAudioDevice(void); // Initialize audio device and context +RLAPI void CloseAudioDevice(void); // Close the audio device and context +RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully +RLAPI void SetMasterVolume(float volume); // Set master volume (listener) + +// Wave/Sound loading/unloading functions +RLAPI Wave LoadWave(const char *fileName); // Load wave data from file +RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav' +RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready +RLAPI Sound LoadSound(const char *fileName); // Load sound from file +RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data +RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready +RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data +RLAPI void UnloadWave(Wave wave); // Unload wave data +RLAPI void UnloadSound(Sound sound); // Unload sound +RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success +RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success + +// Wave/Sound management functions +RLAPI void PlaySound(Sound sound); // Play a sound +RLAPI void StopSound(Sound sound); // Stop playing a sound +RLAPI void PauseSound(Sound sound); // Pause a sound +RLAPI void ResumeSound(Sound sound); // Resume a paused sound +RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing +RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) +RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) +RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (0.5 is center) +RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave +RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range +RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format +RLAPI float *LoadWaveSamples(Wave wave); // Load samples data from wave as a 32bit float data array +RLAPI void UnloadWaveSamples(float *samples); // Unload samples data loaded with LoadWaveSamples() + +// Music management functions +RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file +RLAPI Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize); // Load music stream from data +RLAPI bool IsMusicReady(Music music); // Checks if a music stream is ready +RLAPI void UnloadMusicStream(Music music); // Unload music stream +RLAPI void PlayMusicStream(Music music); // Start music playing +RLAPI bool IsMusicStreamPlaying(Music music); // Check if music is playing +RLAPI void UpdateMusicStream(Music music); // Updates buffers for music streaming +RLAPI void StopMusicStream(Music music); // Stop music playing +RLAPI void PauseMusicStream(Music music); // Pause music playing +RLAPI void ResumeMusicStream(Music music); // Resume playing paused music +RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds) +RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) +RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) +RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (0.5 is center) +RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds) +RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) + +// AudioStream management functions +RLAPI AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Load audio stream (to stream raw audio pcm data) +RLAPI bool IsAudioStreamReady(AudioStream stream); // Checks if an audio stream is ready +RLAPI void UnloadAudioStream(AudioStream stream); // Unload audio stream and free memory +RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int frameCount); // Update audio stream buffers with data +RLAPI bool IsAudioStreamProcessed(AudioStream stream); // Check if any audio stream buffers requires refill +RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream +RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream +RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream +RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing +RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream +RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) +RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) +RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered) +RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams +RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data + +RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream +RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream + +RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline +RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline + +#if defined(__cplusplus) +} +#endif + +#endif // RAYLIB_H diff --git a/build/vendor/raylib/raymath.h b/modules/vendor/raylib/raymath.h similarity index 96% rename from build/vendor/raylib/raymath.h rename to modules/vendor/raylib/raymath.h index 422a42e..8eb2849 100644 --- a/build/vendor/raylib/raymath.h +++ b/modules/vendor/raylib/raymath.h @@ -1,2134 +1,2134 @@ -/********************************************************************************************** -* -* raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions -* -* CONFIGURATION: -* -* #define RAYMATH_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* #define RAYMATH_STATIC_INLINE -* Define static inline functions code, so #include header suffices for use. -* This may use up lots of memory. -* -* CONVENTIONS: -* -* - Functions are always self-contained, no function use another raymath function inside, -* required code is directly re-implemented inside -* - Functions input parameters are always received by value (2 unavoidable exceptions) -* - Functions use always a "result" variable for return -* - Functions are always defined inline -* - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RAYMATH_H -#define RAYMATH_H - -#if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_STATIC_INLINE) - #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_STATIC_INLINE is contradictory" -#endif - -// Function specifiers definition -#if defined(RAYMATH_IMPLEMENTATION) - #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) - #define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll). - #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) - #define RMAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) - #else - #define RMAPI extern inline // Provide external definition - #endif -#elif defined(RAYMATH_STATIC_INLINE) - #define RMAPI static inline // Functions may be inlined, no external out-of-line definition -#else - #if defined(__TINYC__) - #define RMAPI static inline // plain inline not supported by tinycc (See issue #435) - #else - #define RMAPI inline // Functions may be inlined or external definition used - #endif -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846f -#endif - -#ifndef EPSILON - #define EPSILON 0.000001f -#endif - -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif - -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif - -// Get float vector for Matrix -#ifndef MatrixToFloat - #define MatrixToFloat(mat) (MatrixToFloatV(mat).v) -#endif - -// Get float vector for Vector3 -#ifndef Vector3ToFloat - #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -#if !defined(RL_VECTOR2_TYPE) -// Vector2 type -typedef struct Vector2 { - float x; - float y; -} Vector2; -#define RL_VECTOR2_TYPE -#endif - -#if !defined(RL_VECTOR3_TYPE) -// Vector3 type -typedef struct Vector3 { - float x; - float y; - float z; -} Vector3; -#define RL_VECTOR3_TYPE -#endif - -#if !defined(RL_VECTOR4_TYPE) -// Vector4 type -typedef struct Vector4 { - float x; - float y; - float z; - float w; -} Vector4; -#define RL_VECTOR4_TYPE -#endif - -#if !defined(RL_QUATERNION_TYPE) -// Quaternion type -typedef Vector4 Quaternion; -#define RL_QUATERNION_TYPE -#endif - -#if !defined(RL_MATRIX_TYPE) -// Matrix type (OpenGL style 4x4 - right handed, column major) -typedef struct Matrix { - float m0, m4, m8, m12; // Matrix first row (4 components) - float m1, m5, m9, m13; // Matrix second row (4 components) - float m2, m6, m10, m14; // Matrix third row (4 components) - float m3, m7, m11, m15; // Matrix fourth row (4 components) -} Matrix; -#define RL_MATRIX_TYPE -#endif - -// NOTE: Helper types to be used instead of array return types for *ToFloat functions -typedef struct float3 { - float v[3]; -} float3; - -typedef struct float16 { - float v[16]; -} float16; - -#include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabs() - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Utils math -//---------------------------------------------------------------------------------- - -// Clamp float value -RMAPI float Clamp(float value, float min, float max) -{ - float result = (value < min)? min : value; - - if (result > max) result = max; - - return result; -} - -// Calculate linear interpolation between two floats -RMAPI float Lerp(float start, float end, float amount) -{ - float result = start + amount*(end - start); - - return result; -} - -// Normalize input value within input range -RMAPI float Normalize(float value, float start, float end) -{ - float result = (value - start)/(end - start); - - return result; -} - -// Remap input value within input range to output range -RMAPI float Remap(float value, float inputStart, float inputEnd, float outputStart, float outputEnd) -{ - float result = (value - inputStart)/(inputEnd - inputStart)*(outputEnd - outputStart) + outputStart; - - return result; -} - -// Wrap input value from min to max -RMAPI float Wrap(float value, float min, float max) -{ - float result = value - (max - min)*floorf((value - min)/(max - min)); - - return result; -} - -// Check whether two given floats are almost equal -RMAPI int FloatEquals(float x, float y) -{ - int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); - - return result; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector2 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMAPI Vector2 Vector2Zero(void) -{ - Vector2 result = { 0.0f, 0.0f }; - - return result; -} - -// Vector with components value 1.0f -RMAPI Vector2 Vector2One(void) -{ - Vector2 result = { 1.0f, 1.0f }; - - return result; -} - -// Add two vectors (v1 + v2) -RMAPI Vector2 Vector2Add(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x + v2.x, v1.y + v2.y }; - - return result; -} - -// Add vector and float value -RMAPI Vector2 Vector2AddValue(Vector2 v, float add) -{ - Vector2 result = { v.x + add, v.y + add }; - - return result; -} - -// Subtract two vectors (v1 - v2) -RMAPI Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x - v2.x, v1.y - v2.y }; - - return result; -} - -// Subtract vector by float value -RMAPI Vector2 Vector2SubtractValue(Vector2 v, float sub) -{ - Vector2 result = { v.x - sub, v.y - sub }; - - return result; -} - -// Calculate vector length -RMAPI float Vector2Length(Vector2 v) -{ - float result = sqrtf((v.x*v.x) + (v.y*v.y)); - - return result; -} - -// Calculate vector square length -RMAPI float Vector2LengthSqr(Vector2 v) -{ - float result = (v.x*v.x) + (v.y*v.y); - - return result; -} - -// Calculate two vectors dot product -RMAPI float Vector2DotProduct(Vector2 v1, Vector2 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y); - - return result; -} - -// Calculate distance between two vectors -RMAPI float Vector2Distance(Vector2 v1, Vector2 v2) -{ - float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); - - return result; -} - -// Calculate square distance between two vectors -RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) -{ - float result = ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); - - return result; -} - -// Calculate angle between two vectors -// NOTE: Angle is calculated from origin point (0, 0) -RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) -{ - float result = atan2f(v2.y - v1.y, v2.x - v1.x); - - return result; -} - -// Calculate angle defined by a two vectors line -// NOTE: Parameters need to be normalized -// Current implementation should be aligned with glm::angle -RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) -{ - float result = 0.0f; - - float dot = start.x*end.x + start.y*end.y; // Dot product - - float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp - if (dotClamp > 1.0f) dotClamp = 1.0f; - - result = acosf(dotClamp); - - // Alternative implementation, more costly - //float v1Length = sqrtf((start.x*start.x) + (start.y*start.y)); - //float v2Length = sqrtf((end.x*end.x) + (end.y*end.y)); - //float result = -acosf((start.x*end.x + start.y*end.y)/(v1Length*v2Length)); - - return result; -} - -// Scale vector (multiply by value) -RMAPI Vector2 Vector2Scale(Vector2 v, float scale) -{ - Vector2 result = { v.x*scale, v.y*scale }; - - return result; -} - -// Multiply vector by vector -RMAPI Vector2 Vector2Multiply(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x*v2.x, v1.y*v2.y }; - - return result; -} - -// Negate vector -RMAPI Vector2 Vector2Negate(Vector2 v) -{ - Vector2 result = { -v.x, -v.y }; - - return result; -} - -// Divide vector by vector -RMAPI Vector2 Vector2Divide(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x/v2.x, v1.y/v2.y }; - - return result; -} - -// Normalize provided vector -RMAPI Vector2 Vector2Normalize(Vector2 v) -{ - Vector2 result = { 0 }; - float length = sqrtf((v.x*v.x) + (v.y*v.y)); - - if (length > 0) - { - float ilength = 1.0f/length; - result.x = v.x*ilength; - result.y = v.y*ilength; - } - - return result; -} - -// Transforms a Vector2 by a given Matrix -RMAPI Vector2 Vector2Transform(Vector2 v, Matrix mat) -{ - Vector2 result = { 0 }; - - float x = v.x; - float y = v.y; - float z = 0; - - result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; - result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; - - return result; -} - -// Calculate linear interpolation between two vectors -RMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount) -{ - Vector2 result = { 0 }; - - result.x = v1.x + amount*(v2.x - v1.x); - result.y = v1.y + amount*(v2.y - v1.y); - - return result; -} - -// Calculate reflected vector to normal -RMAPI Vector2 Vector2Reflect(Vector2 v, Vector2 normal) -{ - Vector2 result = { 0 }; - - float dotProduct = (v.x*normal.x + v.y*normal.y); // Dot product - - result.x = v.x - (2.0f*normal.x)*dotProduct; - result.y = v.y - (2.0f*normal.y)*dotProduct; - - return result; -} - -// Rotate vector by angle -RMAPI Vector2 Vector2Rotate(Vector2 v, float angle) -{ - Vector2 result = { 0 }; - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.x = v.x*cosres - v.y*sinres; - result.y = v.x*sinres + v.y*cosres; - - return result; -} - -// Move Vector towards target -RMAPI Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance) -{ - Vector2 result = { 0 }; - - float dx = target.x - v.x; - float dy = target.y - v.y; - float value = (dx*dx) + (dy*dy); - - if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; - - float dist = sqrtf(value); - - result.x = v.x + dx/dist*maxDistance; - result.y = v.y + dy/dist*maxDistance; - - return result; -} - -// Invert the given vector -RMAPI Vector2 Vector2Invert(Vector2 v) -{ - Vector2 result = { 1.0f/v.x, 1.0f/v.y }; - - return result; -} - -// Clamp the components of the vector between -// min and max values specified by the given vectors -RMAPI Vector2 Vector2Clamp(Vector2 v, Vector2 min, Vector2 max) -{ - Vector2 result = { 0 }; - - result.x = fminf(max.x, fmaxf(min.x, v.x)); - result.y = fminf(max.y, fmaxf(min.y, v.y)); - - return result; -} - -// Clamp the magnitude of the vector between two min and max values -RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max) -{ - Vector2 result = v; - - float length = (v.x*v.x) + (v.y*v.y); - if (length > 0.0f) - { - length = sqrtf(length); - - if (length < min) - { - float scale = min/length; - result.x = v.x*scale; - result.y = v.y*scale; - } - else if (length > max) - { - float scale = max/length; - result.x = v.x*scale; - result.y = v.y*scale; - } - } - - return result; -} - -// Check whether two given vectors are almost equal -RMAPI int Vector2Equals(Vector2 p, Vector2 q) -{ - int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))); - - return result; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vector3 math -//---------------------------------------------------------------------------------- - -// Vector with components value 0.0f -RMAPI Vector3 Vector3Zero(void) -{ - Vector3 result = { 0.0f, 0.0f, 0.0f }; - - return result; -} - -// Vector with components value 1.0f -RMAPI Vector3 Vector3One(void) -{ - Vector3 result = { 1.0f, 1.0f, 1.0f }; - - return result; -} - -// Add two vectors -RMAPI Vector3 Vector3Add(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; - - return result; -} - -// Add vector and float value -RMAPI Vector3 Vector3AddValue(Vector3 v, float add) -{ - Vector3 result = { v.x + add, v.y + add, v.z + add }; - - return result; -} - -// Subtract two vectors -RMAPI Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; - - return result; -} - -// Subtract vector by float value -RMAPI Vector3 Vector3SubtractValue(Vector3 v, float sub) -{ - Vector3 result = { v.x - sub, v.y - sub, v.z - sub }; - - return result; -} - -// Multiply vector by scalar -RMAPI Vector3 Vector3Scale(Vector3 v, float scalar) -{ - Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; - - return result; -} - -// Multiply vector by vector -RMAPI Vector3 Vector3Multiply(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; - - return result; -} - -// Calculate two vectors cross product -RMAPI Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; - - return result; -} - -// Calculate one vector perpendicular vector -RMAPI Vector3 Vector3Perpendicular(Vector3 v) -{ - Vector3 result = { 0 }; - - float min = (float) fabs(v.x); - Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; - - if (fabsf(v.y) < min) - { - min = (float) fabs(v.y); - Vector3 tmp = {0.0f, 1.0f, 0.0f}; - cardinalAxis = tmp; - } - - if (fabsf(v.z) < min) - { - Vector3 tmp = {0.0f, 0.0f, 1.0f}; - cardinalAxis = tmp; - } - - // Cross product between vectors - result.x = v.y*cardinalAxis.z - v.z*cardinalAxis.y; - result.y = v.z*cardinalAxis.x - v.x*cardinalAxis.z; - result.z = v.x*cardinalAxis.y - v.y*cardinalAxis.x; - - return result; -} - -// Calculate vector length -RMAPI float Vector3Length(const Vector3 v) -{ - float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - - return result; -} - -// Calculate vector square length -RMAPI float Vector3LengthSqr(const Vector3 v) -{ - float result = v.x*v.x + v.y*v.y + v.z*v.z; - - return result; -} - -// Calculate two vectors dot product -RMAPI float Vector3DotProduct(Vector3 v1, Vector3 v2) -{ - float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); - - return result; -} - -// Calculate distance between two vectors -RMAPI float Vector3Distance(Vector3 v1, Vector3 v2) -{ - float result = 0.0f; - - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - result = sqrtf(dx*dx + dy*dy + dz*dz); - - return result; -} - -// Calculate square distance between two vectors -RMAPI float Vector3DistanceSqr(Vector3 v1, Vector3 v2) -{ - float result = 0.0f; - - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - result = dx*dx + dy*dy + dz*dz; - - return result; -} - -// Calculate angle between two vectors -RMAPI float Vector3Angle(Vector3 v1, Vector3 v2) -{ - float result = 0.0f; - - Vector3 cross = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; - float len = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z); - float dot = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); - result = atan2f(len, dot); - - return result; -} - -// Negate provided vector (invert direction) -RMAPI Vector3 Vector3Negate(Vector3 v) -{ - Vector3 result = { -v.x, -v.y, -v.z }; - - return result; -} - -// Divide vector by vector -RMAPI Vector3 Vector3Divide(Vector3 v1, Vector3 v2) -{ - Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; - - return result; -} - -// Normalize provided vector -RMAPI Vector3 Vector3Normalize(Vector3 v) -{ - Vector3 result = v; - - float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - - result.x *= ilength; - result.y *= ilength; - result.z *= ilength; - - return result; -} - -// Orthonormalize provided vectors -// Makes vectors normalized and orthogonal to each other -// Gram-Schmidt function implementation -RMAPI void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2) -{ - float length = 0.0f; - float ilength = 0.0f; - - // Vector3Normalize(*v1); - Vector3 v = *v1; - length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - v1->x *= ilength; - v1->y *= ilength; - v1->z *= ilength; - - // Vector3CrossProduct(*v1, *v2) - Vector3 vn1 = { v1->y*v2->z - v1->z*v2->y, v1->z*v2->x - v1->x*v2->z, v1->x*v2->y - v1->y*v2->x }; - - // Vector3Normalize(vn1); - v = vn1; - length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - vn1.x *= ilength; - vn1.y *= ilength; - vn1.z *= ilength; - - // Vector3CrossProduct(vn1, *v1) - Vector3 vn2 = { vn1.y*v1->z - vn1.z*v1->y, vn1.z*v1->x - vn1.x*v1->z, vn1.x*v1->y - vn1.y*v1->x }; - - *v2 = vn2; -} - -// Transforms a Vector3 by a given Matrix -RMAPI Vector3 Vector3Transform(Vector3 v, Matrix mat) -{ - Vector3 result = { 0 }; - - float x = v.x; - float y = v.y; - float z = v.z; - - result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; - result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; - result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; - - return result; -} - -// Transform a vector by quaternion rotation -RMAPI Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) -{ - Vector3 result = { 0 }; - - result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); - result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); - result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); - - return result; -} - -// Rotates a vector around an axis -RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) -{ - // Using Euler-Rodrigues Formula - // Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula - - Vector3 result = v; - - // Vector3Normalize(axis); - float length = sqrtf(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f / length; - axis.x *= ilength; - axis.y *= ilength; - axis.z *= ilength; - - angle /= 2.0f; - float a = sinf(angle); - float b = axis.x * a; - float c = axis.y * a; - float d = axis.z * a; - a = cosf(angle); - Vector3 w = { b, c, d }; - - // Vector3CrossProduct(w, v) - Vector3 wv = { w.y * v.z - w.z * v.y, w.z * v.x - w.x * v.z, w.x * v.y - w.y * v.x }; - - // Vector3CrossProduct(w, wv) - Vector3 wwv = { w.y * wv.z - w.z * wv.y, w.z * wv.x - w.x * wv.z, w.x * wv.y - w.y * wv.x }; - - // Vector3Scale(wv, 2 * a) - a *= 2; - wv.x *= a; - wv.y *= a; - wv.z *= a; - - // Vector3Scale(wwv, 2) - wwv.x *= 2; - wwv.y *= 2; - wwv.z *= 2; - - result.x += wv.x; - result.y += wv.y; - result.z += wv.z; - - result.x += wwv.x; - result.y += wwv.y; - result.z += wwv.z; - - return result; -} - -// Calculate linear interpolation between two vectors -RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) -{ - Vector3 result = { 0 }; - - result.x = v1.x + amount*(v2.x - v1.x); - result.y = v1.y + amount*(v2.y - v1.y); - result.z = v1.z + amount*(v2.z - v1.z); - - return result; -} - -// Calculate reflected vector to normal -RMAPI Vector3 Vector3Reflect(Vector3 v, Vector3 normal) -{ - Vector3 result = { 0 }; - - // I is the original vector - // N is the normal of the incident plane - // R = I - (2*N*(DotProduct[I, N])) - - float dotProduct = (v.x*normal.x + v.y*normal.y + v.z*normal.z); - - result.x = v.x - (2.0f*normal.x)*dotProduct; - result.y = v.y - (2.0f*normal.y)*dotProduct; - result.z = v.z - (2.0f*normal.z)*dotProduct; - - return result; -} - -// Get min value for each pair of components -RMAPI Vector3 Vector3Min(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fminf(v1.x, v2.x); - result.y = fminf(v1.y, v2.y); - result.z = fminf(v1.z, v2.z); - - return result; -} - -// Get max value for each pair of components -RMAPI Vector3 Vector3Max(Vector3 v1, Vector3 v2) -{ - Vector3 result = { 0 }; - - result.x = fmaxf(v1.x, v2.x); - result.y = fmaxf(v1.y, v2.y); - result.z = fmaxf(v1.z, v2.z); - - return result; -} - -// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) -// NOTE: Assumes P is on the plane of the triangle -RMAPI Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) -{ - Vector3 result = { 0 }; - - Vector3 v0 = { b.x - a.x, b.y - a.y, b.z - a.z }; // Vector3Subtract(b, a) - Vector3 v1 = { c.x - a.x, c.y - a.y, c.z - a.z }; // Vector3Subtract(c, a) - Vector3 v2 = { p.x - a.x, p.y - a.y, p.z - a.z }; // Vector3Subtract(p, a) - float d00 = (v0.x*v0.x + v0.y*v0.y + v0.z*v0.z); // Vector3DotProduct(v0, v0) - float d01 = (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z); // Vector3DotProduct(v0, v1) - float d11 = (v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); // Vector3DotProduct(v1, v1) - float d20 = (v2.x*v0.x + v2.y*v0.y + v2.z*v0.z); // Vector3DotProduct(v2, v0) - float d21 = (v2.x*v1.x + v2.y*v1.y + v2.z*v1.z); // Vector3DotProduct(v2, v1) - - float denom = d00*d11 - d01*d01; - - result.y = (d11*d20 - d01*d21)/denom; - result.z = (d00*d21 - d01*d20)/denom; - result.x = 1.0f - (result.z + result.y); - - return result; -} - -// Projects a Vector3 from screen space into object space -// NOTE: We are avoiding calling other raymath functions despite available -RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) -{ - Vector3 result = { 0 }; - - // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it - Matrix matViewProj = { // MatrixMultiply(view, projection); - view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, - view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, - view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14, - view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15, - view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12, - view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13, - view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14, - view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15, - view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12, - view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13, - view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14, - view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15, - view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12, - view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13, - view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14, - view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 }; - - // Calculate inverted matrix -> MatrixInvert(matViewProj); - // Cache the matrix values (speed optimization) - float a00 = matViewProj.m0, a01 = matViewProj.m1, a02 = matViewProj.m2, a03 = matViewProj.m3; - float a10 = matViewProj.m4, a11 = matViewProj.m5, a12 = matViewProj.m6, a13 = matViewProj.m7; - float a20 = matViewProj.m8, a21 = matViewProj.m9, a22 = matViewProj.m10, a23 = matViewProj.m11; - float a30 = matViewProj.m12, a31 = matViewProj.m13, a32 = matViewProj.m14, a33 = matViewProj.m15; - - float b00 = a00*a11 - a01*a10; - float b01 = a00*a12 - a02*a10; - float b02 = a00*a13 - a03*a10; - float b03 = a01*a12 - a02*a11; - float b04 = a01*a13 - a03*a11; - float b05 = a02*a13 - a03*a12; - float b06 = a20*a31 - a21*a30; - float b07 = a20*a32 - a22*a30; - float b08 = a20*a33 - a23*a30; - float b09 = a21*a32 - a22*a31; - float b10 = a21*a33 - a23*a31; - float b11 = a22*a33 - a23*a32; - - // Calculate the invert determinant (inlined to avoid double-caching) - float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); - - Matrix matViewProjInv = { - (a11*b11 - a12*b10 + a13*b09)*invDet, - (-a01*b11 + a02*b10 - a03*b09)*invDet, - (a31*b05 - a32*b04 + a33*b03)*invDet, - (-a21*b05 + a22*b04 - a23*b03)*invDet, - (-a10*b11 + a12*b08 - a13*b07)*invDet, - (a00*b11 - a02*b08 + a03*b07)*invDet, - (-a30*b05 + a32*b02 - a33*b01)*invDet, - (a20*b05 - a22*b02 + a23*b01)*invDet, - (a10*b10 - a11*b08 + a13*b06)*invDet, - (-a00*b10 + a01*b08 - a03*b06)*invDet, - (a30*b04 - a31*b02 + a33*b00)*invDet, - (-a20*b04 + a21*b02 - a23*b00)*invDet, - (-a10*b09 + a11*b07 - a12*b06)*invDet, - (a00*b09 - a01*b07 + a02*b06)*invDet, - (-a30*b03 + a31*b01 - a32*b00)*invDet, - (a20*b03 - a21*b01 + a22*b00)*invDet }; - - // Create quaternion from source point - Quaternion quat = { source.x, source.y, source.z, 1.0f }; - - // Multiply quat point by unprojecte matrix - Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) - matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, - matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, - matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w, - matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w }; - - // Normalized world points in vectors - result.x = qtransformed.x/qtransformed.w; - result.y = qtransformed.y/qtransformed.w; - result.z = qtransformed.z/qtransformed.w; - - return result; -} - -// Get Vector3 as float array -RMAPI float3 Vector3ToFloatV(Vector3 v) -{ - float3 buffer = { 0 }; - - buffer.v[0] = v.x; - buffer.v[1] = v.y; - buffer.v[2] = v.z; - - return buffer; -} - -// Invert the given vector -RMAPI Vector3 Vector3Invert(Vector3 v) -{ - Vector3 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z }; - - return result; -} - -// Clamp the components of the vector between -// min and max values specified by the given vectors -RMAPI Vector3 Vector3Clamp(Vector3 v, Vector3 min, Vector3 max) -{ - Vector3 result = { 0 }; - - result.x = fminf(max.x, fmaxf(min.x, v.x)); - result.y = fminf(max.y, fmaxf(min.y, v.y)); - result.z = fminf(max.z, fmaxf(min.z, v.z)); - - return result; -} - -// Clamp the magnitude of the vector between two values -RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) -{ - Vector3 result = v; - - float length = (v.x*v.x) + (v.y*v.y) + (v.z*v.z); - if (length > 0.0f) - { - length = sqrtf(length); - - if (length < min) - { - float scale = min/length; - result.x = v.x*scale; - result.y = v.y*scale; - result.z = v.z*scale; - } - else if (length > max) - { - float scale = max/length; - result.x = v.x*scale; - result.y = v.y*scale; - result.z = v.z*scale; - } - } - - return result; -} - -// Check whether two given vectors are almost equal -RMAPI int Vector3Equals(Vector3 p, Vector3 q) -{ - int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && - ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); - - return result; -} - -// Compute the direction of a refracted ray where v specifies the -// normalized direction of the incoming ray, n specifies the -// normalized normal vector of the interface of two optical media, -// and r specifies the ratio of the refractive index of the medium -// from where the ray comes to the refractive index of the medium -// on the other side of the surface -RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) -{ - Vector3 result = { 0 }; - - float dot = v.x*n.x + v.y*n.y + v.z*n.z; - float d = 1.0f - r*r*(1.0f - dot*dot); - - if (d >= 0.0f) - { - d = sqrtf(d); - v.x = r*v.x - (r*dot + d)*n.x; - v.y = r*v.y - (r*dot + d)*n.y; - v.z = r*v.z - (r*dot + d)*n.z; - - result = v; - } - - return result; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Matrix math -//---------------------------------------------------------------------------------- - -// Compute matrix determinant -RMAPI float MatrixDeterminant(Matrix mat) -{ - float result = 0.0f; - - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + - a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + - a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + - a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + - a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + - a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; - - return result; -} - -// Get the trace of the matrix (sum of the values along the diagonal) -RMAPI float MatrixTrace(Matrix mat) -{ - float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); - - return result; -} - -// Transposes provided matrix -RMAPI Matrix MatrixTranspose(Matrix mat) -{ - Matrix result = { 0 }; - - result.m0 = mat.m0; - result.m1 = mat.m4; - result.m2 = mat.m8; - result.m3 = mat.m12; - result.m4 = mat.m1; - result.m5 = mat.m5; - result.m6 = mat.m9; - result.m7 = mat.m13; - result.m8 = mat.m2; - result.m9 = mat.m6; - result.m10 = mat.m10; - result.m11 = mat.m14; - result.m12 = mat.m3; - result.m13 = mat.m7; - result.m14 = mat.m11; - result.m15 = mat.m15; - - return result; -} - -// Invert provided matrix -RMAPI Matrix MatrixInvert(Matrix mat) -{ - Matrix result = { 0 }; - - // Cache the matrix values (speed optimization) - float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; - float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; - float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; - float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; - - float b00 = a00*a11 - a01*a10; - float b01 = a00*a12 - a02*a10; - float b02 = a00*a13 - a03*a10; - float b03 = a01*a12 - a02*a11; - float b04 = a01*a13 - a03*a11; - float b05 = a02*a13 - a03*a12; - float b06 = a20*a31 - a21*a30; - float b07 = a20*a32 - a22*a30; - float b08 = a20*a33 - a23*a30; - float b09 = a21*a32 - a22*a31; - float b10 = a21*a33 - a23*a31; - float b11 = a22*a33 - a23*a32; - - // Calculate the invert determinant (inlined to avoid double-caching) - float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); - - result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; - result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; - result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; - result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; - result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; - result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; - result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; - result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; - result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; - result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; - result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; - result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; - result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; - result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; - result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; - result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; - - return result; -} - -// Get identity matrix -RMAPI Matrix MatrixIdentity(void) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Add two matrices -RMAPI Matrix MatrixAdd(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0 + right.m0; - result.m1 = left.m1 + right.m1; - result.m2 = left.m2 + right.m2; - result.m3 = left.m3 + right.m3; - result.m4 = left.m4 + right.m4; - result.m5 = left.m5 + right.m5; - result.m6 = left.m6 + right.m6; - result.m7 = left.m7 + right.m7; - result.m8 = left.m8 + right.m8; - result.m9 = left.m9 + right.m9; - result.m10 = left.m10 + right.m10; - result.m11 = left.m11 + right.m11; - result.m12 = left.m12 + right.m12; - result.m13 = left.m13 + right.m13; - result.m14 = left.m14 + right.m14; - result.m15 = left.m15 + right.m15; - - return result; -} - -// Subtract two matrices (left - right) -RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0 - right.m0; - result.m1 = left.m1 - right.m1; - result.m2 = left.m2 - right.m2; - result.m3 = left.m3 - right.m3; - result.m4 = left.m4 - right.m4; - result.m5 = left.m5 - right.m5; - result.m6 = left.m6 - right.m6; - result.m7 = left.m7 - right.m7; - result.m8 = left.m8 - right.m8; - result.m9 = left.m9 - right.m9; - result.m10 = left.m10 - right.m10; - result.m11 = left.m11 - right.m11; - result.m12 = left.m12 - right.m12; - result.m13 = left.m13 - right.m13; - result.m14 = left.m14 - right.m14; - result.m15 = left.m15 - right.m15; - - return result; -} - -// Get two matrix multiplication -// NOTE: When multiplying matrices... the order matters! -RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; - result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; - result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; - result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; - result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; - result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; - result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; - result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; - result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; - result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; - result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; - result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; - result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; - result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; - result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; - result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; - - return result; -} - -// Get translation matrix -RMAPI Matrix MatrixTranslate(float x, float y, float z) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, x, - 0.0f, 1.0f, 0.0f, y, - 0.0f, 0.0f, 1.0f, z, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Create rotation matrix from axis and angle -// NOTE: Angle should be provided in radians -RMAPI Matrix MatrixRotate(Vector3 axis, float angle) -{ - Matrix result = { 0 }; - - float x = axis.x, y = axis.y, z = axis.z; - - float lengthSquared = x*x + y*y + z*z; - - if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) - { - float ilength = 1.0f/sqrtf(lengthSquared); - x *= ilength; - y *= ilength; - z *= ilength; - } - - float sinres = sinf(angle); - float cosres = cosf(angle); - float t = 1.0f - cosres; - - result.m0 = x*x*t + cosres; - result.m1 = y*x*t + z*sinres; - result.m2 = z*x*t - y*sinres; - result.m3 = 0.0f; - - result.m4 = x*y*t - z*sinres; - result.m5 = y*y*t + cosres; - result.m6 = z*y*t + x*sinres; - result.m7 = 0.0f; - - result.m8 = x*z*t + y*sinres; - result.m9 = y*z*t - x*sinres; - result.m10 = z*z*t + cosres; - result.m11 = 0.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = 0.0f; - result.m15 = 1.0f; - - return result; -} - -// Get x-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateX(float angle) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m5 = cosres; - result.m6 = sinres; - result.m9 = -sinres; - result.m10 = cosres; - - return result; -} - -// Get y-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateY(float angle) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m2 = -sinres; - result.m8 = sinres; - result.m10 = cosres; - - return result; -} - -// Get z-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateZ(float angle) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float cosres = cosf(angle); - float sinres = sinf(angle); - - result.m0 = cosres; - result.m1 = sinres; - result.m4 = -sinres; - result.m5 = cosres; - - return result; -} - - -// Get xyz-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateXYZ(Vector3 angle) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float cosz = cosf(-angle.z); - float sinz = sinf(-angle.z); - float cosy = cosf(-angle.y); - float siny = sinf(-angle.y); - float cosx = cosf(-angle.x); - float sinx = sinf(-angle.x); - - result.m0 = cosz*cosy; - result.m1 = (cosz*siny*sinx) - (sinz*cosx); - result.m2 = (cosz*siny*cosx) + (sinz*sinx); - - result.m4 = sinz*cosy; - result.m5 = (sinz*siny*sinx) + (cosz*cosx); - result.m6 = (sinz*siny*cosx) - (cosz*sinx); - - result.m8 = -siny; - result.m9 = cosy*sinx; - result.m10= cosy*cosx; - - return result; -} - -// Get zyx-rotation matrix -// NOTE: Angle must be provided in radians -RMAPI Matrix MatrixRotateZYX(Vector3 angle) -{ - Matrix result = { 0 }; - - float cz = cosf(angle.z); - float sz = sinf(angle.z); - float cy = cosf(angle.y); - float sy = sinf(angle.y); - float cx = cosf(angle.x); - float sx = sinf(angle.x); - - result.m0 = cz*cy; - result.m4 = cz*sy*sx - cx*sz; - result.m8 = sz*sx + cz*cx*sy; - result.m12 = 0; - - result.m1 = cy*sz; - result.m5 = cz*cx + sz*sy*sx; - result.m9 = cx*sz*sy - cz*sx; - result.m13 = 0; - - result.m2 = -sy; - result.m6 = cy*sx; - result.m10 = cy*cx; - result.m14 = 0; - - result.m3 = 0; - result.m7 = 0; - result.m11 = 0; - result.m15 = 1; - - return result; -} - -// Get scaling matrix -RMAPI Matrix MatrixScale(float x, float y, float z) -{ - Matrix result = { x, 0.0f, 0.0f, 0.0f, - 0.0f, y, 0.0f, 0.0f, - 0.0f, 0.0f, z, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Get perspective projection matrix -RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far) -{ - Matrix result = { 0 }; - - float rl = (float)(right - left); - float tb = (float)(top - bottom); - float fn = (float)(far - near); - - result.m0 = ((float)near*2.0f)/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - - result.m4 = 0.0f; - result.m5 = ((float)near*2.0f)/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - - result.m8 = ((float)right + (float)left)/rl; - result.m9 = ((float)top + (float)bottom)/tb; - result.m10 = -((float)far + (float)near)/fn; - result.m11 = -1.0f; - - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = -((float)far*(float)near*2.0f)/fn; - result.m15 = 0.0f; - - return result; -} - -// Get perspective projection matrix -// NOTE: Fovy angle must be provided in radians -RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double far) -{ - Matrix result = { 0 }; - - double top = near*tan(fovy*0.5); - double bottom = -top; - double right = top*aspect; - double left = -right; - - // MatrixFrustum(-right, right, -top, top, near, far); - float rl = (float)(right - left); - float tb = (float)(top - bottom); - float fn = (float)(far - near); - - result.m0 = ((float)near*2.0f)/rl; - result.m5 = ((float)near*2.0f)/tb; - result.m8 = ((float)right + (float)left)/rl; - result.m9 = ((float)top + (float)bottom)/tb; - result.m10 = -((float)far + (float)near)/fn; - result.m11 = -1.0f; - result.m14 = -((float)far*(float)near*2.0f)/fn; - - return result; -} - -// Get orthographic projection matrix -RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far) -{ - Matrix result = { 0 }; - - float rl = (float)(right - left); - float tb = (float)(top - bottom); - float fn = (float)(far - near); - - result.m0 = 2.0f/rl; - result.m1 = 0.0f; - result.m2 = 0.0f; - result.m3 = 0.0f; - result.m4 = 0.0f; - result.m5 = 2.0f/tb; - result.m6 = 0.0f; - result.m7 = 0.0f; - result.m8 = 0.0f; - result.m9 = 0.0f; - result.m10 = -2.0f/fn; - result.m11 = 0.0f; - result.m12 = -((float)left + (float)right)/rl; - result.m13 = -((float)top + (float)bottom)/tb; - result.m14 = -((float)far + (float)near)/fn; - result.m15 = 1.0f; - - return result; -} - -// Get camera look-at matrix (view matrix) -RMAPI Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) -{ - Matrix result = { 0 }; - - float length = 0.0f; - float ilength = 0.0f; - - // Vector3Subtract(eye, target) - Vector3 vz = { eye.x - target.x, eye.y - target.y, eye.z - target.z }; - - // Vector3Normalize(vz) - Vector3 v = vz; - length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - vz.x *= ilength; - vz.y *= ilength; - vz.z *= ilength; - - // Vector3CrossProduct(up, vz) - Vector3 vx = { up.y*vz.z - up.z*vz.y, up.z*vz.x - up.x*vz.z, up.x*vz.y - up.y*vz.x }; - - // Vector3Normalize(x) - v = vx; - length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - vx.x *= ilength; - vx.y *= ilength; - vx.z *= ilength; - - // Vector3CrossProduct(vz, vx) - Vector3 vy = { vz.y*vx.z - vz.z*vx.y, vz.z*vx.x - vz.x*vx.z, vz.x*vx.y - vz.y*vx.x }; - - result.m0 = vx.x; - result.m1 = vy.x; - result.m2 = vz.x; - result.m3 = 0.0f; - result.m4 = vx.y; - result.m5 = vy.y; - result.m6 = vz.y; - result.m7 = 0.0f; - result.m8 = vx.z; - result.m9 = vy.z; - result.m10 = vz.z; - result.m11 = 0.0f; - result.m12 = -(vx.x*eye.x + vx.y*eye.y + vx.z*eye.z); // Vector3DotProduct(vx, eye) - result.m13 = -(vy.x*eye.x + vy.y*eye.y + vy.z*eye.z); // Vector3DotProduct(vy, eye) - result.m14 = -(vz.x*eye.x + vz.y*eye.y + vz.z*eye.z); // Vector3DotProduct(vz, eye) - result.m15 = 1.0f; - - return result; -} - -// Get float array of matrix data -RMAPI float16 MatrixToFloatV(Matrix mat) -{ - float16 result = { 0 }; - - result.v[0] = mat.m0; - result.v[1] = mat.m1; - result.v[2] = mat.m2; - result.v[3] = mat.m3; - result.v[4] = mat.m4; - result.v[5] = mat.m5; - result.v[6] = mat.m6; - result.v[7] = mat.m7; - result.v[8] = mat.m8; - result.v[9] = mat.m9; - result.v[10] = mat.m10; - result.v[11] = mat.m11; - result.v[12] = mat.m12; - result.v[13] = mat.m13; - result.v[14] = mat.m14; - result.v[15] = mat.m15; - - return result; -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Quaternion math -//---------------------------------------------------------------------------------- - -// Add two quaternions -RMAPI Quaternion QuaternionAdd(Quaternion q1, Quaternion q2) -{ - Quaternion result = {q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w}; - - return result; -} - -// Add quaternion and float value -RMAPI Quaternion QuaternionAddValue(Quaternion q, float add) -{ - Quaternion result = {q.x + add, q.y + add, q.z + add, q.w + add}; - - return result; -} - -// Subtract two quaternions -RMAPI Quaternion QuaternionSubtract(Quaternion q1, Quaternion q2) -{ - Quaternion result = {q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w}; - - return result; -} - -// Subtract quaternion and float value -RMAPI Quaternion QuaternionSubtractValue(Quaternion q, float sub) -{ - Quaternion result = {q.x - sub, q.y - sub, q.z - sub, q.w - sub}; - - return result; -} - -// Get identity quaternion -RMAPI Quaternion QuaternionIdentity(void) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - - return result; -} - -// Computes the length of a quaternion -RMAPI float QuaternionLength(Quaternion q) -{ - float result = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - - return result; -} - -// Normalize provided quaternion -RMAPI Quaternion QuaternionNormalize(Quaternion q) -{ - Quaternion result = { 0 }; - - float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - - return result; -} - -// Invert provided quaternion -RMAPI Quaternion QuaternionInvert(Quaternion q) -{ - Quaternion result = q; - - float lengthSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; - - if (lengthSq != 0.0f) - { - float invLength = 1.0f/lengthSq; - - result.x *= -invLength; - result.y *= -invLength; - result.z *= -invLength; - result.w *= invLength; - } - - return result; -} - -// Calculate two quaternion multiplication -RMAPI Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) -{ - Quaternion result = { 0 }; - - float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; - float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; - - result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; - result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; - result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; - result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; - - return result; -} - -// Scale quaternion by float value -RMAPI Quaternion QuaternionScale(Quaternion q, float mul) -{ - Quaternion result = { 0 }; - - result.x = q.x*mul; - result.y = q.y*mul; - result.z = q.z*mul; - result.w = q.w*mul; - - return result; -} - -// Divide two quaternions -RMAPI Quaternion QuaternionDivide(Quaternion q1, Quaternion q2) -{ - Quaternion result = { q1.x/q2.x, q1.y/q2.y, q1.z/q2.z, q1.w/q2.w }; - - return result; -} - -// Calculate linear interpolation between two quaternions -RMAPI Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - result.x = q1.x + amount*(q2.x - q1.x); - result.y = q1.y + amount*(q2.y - q1.y); - result.z = q1.z + amount*(q2.z - q1.z); - result.w = q1.w + amount*(q2.w - q1.w); - - return result; -} - -// Calculate slerp-optimized interpolation between two quaternions -RMAPI Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - // QuaternionLerp(q1, q2, amount) - result.x = q1.x + amount*(q2.x - q1.x); - result.y = q1.y + amount*(q2.y - q1.y); - result.z = q1.z + amount*(q2.z - q1.z); - result.w = q1.w + amount*(q2.w - q1.w); - - // QuaternionNormalize(q); - Quaternion q = result; - float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - - return result; -} - -// Calculates spherical linear interpolation between two quaternions -RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = { 0 }; - - float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; - - if (cosHalfTheta < 0) - { - q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w; - cosHalfTheta = -cosHalfTheta; - } - - if (fabsf(cosHalfTheta) >= 1.0f) result = q1; - else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); - else - { - float halfTheta = acosf(cosHalfTheta); - float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); - - if (fabsf(sinHalfTheta) < 0.001f) - { - result.x = (q1.x*0.5f + q2.x*0.5f); - result.y = (q1.y*0.5f + q2.y*0.5f); - result.z = (q1.z*0.5f + q2.z*0.5f); - result.w = (q1.w*0.5f + q2.w*0.5f); - } - else - { - float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; - float ratioB = sinf(amount*halfTheta)/sinHalfTheta; - - result.x = (q1.x*ratioA + q2.x*ratioB); - result.y = (q1.y*ratioA + q2.y*ratioB); - result.z = (q1.z*ratioA + q2.z*ratioB); - result.w = (q1.w*ratioA + q2.w*ratioB); - } - } - - return result; -} - -// Calculate quaternion based on the rotation from one vector to another -RMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) -{ - Quaternion result = { 0 }; - - float cos2Theta = (from.x*to.x + from.y*to.y + from.z*to.z); // Vector3DotProduct(from, to) - Vector3 cross = { from.y*to.z - from.z*to.y, from.z*to.x - from.x*to.z, from.x*to.y - from.y*to.x }; // Vector3CrossProduct(from, to) - - result.x = cross.x; - result.y = cross.y; - result.z = cross.z; - result.w = 1.0f + cos2Theta; - - // QuaternionNormalize(q); - // NOTE: Normalize to essentially nlerp the original and identity to 0.5 - Quaternion q = result; - float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - - return result; -} - -// Get a quaternion for a given rotation matrix -RMAPI Quaternion QuaternionFromMatrix(Matrix mat) -{ - Quaternion result = { 0 }; - - float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; - float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; - float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; - float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5; - - int biggestIndex = 0; - float fourBiggestSquaredMinus1 = fourWSquaredMinus1; - if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) - { - fourBiggestSquaredMinus1 = fourXSquaredMinus1; - biggestIndex = 1; - } - - if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) - { - fourBiggestSquaredMinus1 = fourYSquaredMinus1; - biggestIndex = 2; - } - - if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) - { - fourBiggestSquaredMinus1 = fourZSquaredMinus1; - biggestIndex = 3; - } - - float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f) * 0.5f; - float mult = 0.25f / biggestVal; - - switch (biggestIndex) - { - case 0: - result.w = biggestVal; - result.x = (mat.m6 - mat.m9) * mult; - result.y = (mat.m8 - mat.m2) * mult; - result.z = (mat.m1 - mat.m4) * mult; - break; - case 1: - result.x = biggestVal; - result.w = (mat.m6 - mat.m9) * mult; - result.y = (mat.m1 + mat.m4) * mult; - result.z = (mat.m8 + mat.m2) * mult; - break; - case 2: - result.y = biggestVal; - result.w = (mat.m8 - mat.m2) * mult; - result.x = (mat.m1 + mat.m4) * mult; - result.z = (mat.m6 + mat.m9) * mult; - break; - case 3: - result.z = biggestVal; - result.w = (mat.m1 - mat.m4) * mult; - result.x = (mat.m8 + mat.m2) * mult; - result.y = (mat.m6 + mat.m9) * mult; - break; - } - - return result; -} - -// Get a matrix for a given quaternion -RMAPI Matrix QuaternionToMatrix(Quaternion q) -{ - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() - - float a2 = q.x*q.x; - float b2 = q.y*q.y; - float c2 = q.z*q.z; - float ac = q.x*q.z; - float ab = q.x*q.y; - float bc = q.y*q.z; - float ad = q.w*q.x; - float bd = q.w*q.y; - float cd = q.w*q.z; - - result.m0 = 1 - 2*(b2 + c2); - result.m1 = 2*(ab + cd); - result.m2 = 2*(ac - bd); - - result.m4 = 2*(ab - cd); - result.m5 = 1 - 2*(a2 + c2); - result.m6 = 2*(bc + ad); - - result.m8 = 2*(ac + bd); - result.m9 = 2*(bc - ad); - result.m10 = 1 - 2*(a2 + b2); - - return result; -} - -// Get rotation quaternion for an angle and axis -// NOTE: Angle must be provided in radians -RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) -{ - Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - - float axisLength = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); - - if (axisLength != 0.0f) - { - angle *= 0.5f; - - float length = 0.0f; - float ilength = 0.0f; - - // Vector3Normalize(axis) - Vector3 v = axis; - length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - axis.x *= ilength; - axis.y *= ilength; - axis.z *= ilength; - - float sinres = sinf(angle); - float cosres = cosf(angle); - - result.x = axis.x*sinres; - result.y = axis.y*sinres; - result.z = axis.z*sinres; - result.w = cosres; - - // QuaternionNormalize(q); - Quaternion q = result; - length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - ilength = 1.0f/length; - result.x = q.x*ilength; - result.y = q.y*ilength; - result.z = q.z*ilength; - result.w = q.w*ilength; - } - - return result; -} - -// Get the rotation angle and axis for a given quaternion -RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) -{ - if (fabsf(q.w) > 1.0f) - { - // QuaternionNormalize(q); - float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; - - q.x = q.x*ilength; - q.y = q.y*ilength; - q.z = q.z*ilength; - q.w = q.w*ilength; - } - - Vector3 resAxis = { 0.0f, 0.0f, 0.0f }; - float resAngle = 2.0f*acosf(q.w); - float den = sqrtf(1.0f - q.w*q.w); - - if (den > 0.0001f) - { - resAxis.x = q.x/den; - resAxis.y = q.y/den; - resAxis.z = q.z/den; - } - else - { - // This occurs when the angle is zero. - // Not a problem: just set an arbitrary normalized axis. - resAxis.x = 1.0f; - } - - *outAxis = resAxis; - *outAngle = resAngle; -} - -// Get the quaternion equivalent to Euler angles -// NOTE: Rotation order is ZYX -RMAPI Quaternion QuaternionFromEuler(float pitch, float yaw, float roll) -{ - Quaternion result = { 0 }; - - float x0 = cosf(pitch*0.5f); - float x1 = sinf(pitch*0.5f); - float y0 = cosf(yaw*0.5f); - float y1 = sinf(yaw*0.5f); - float z0 = cosf(roll*0.5f); - float z1 = sinf(roll*0.5f); - - result.x = x1*y0*z0 - x0*y1*z1; - result.y = x0*y1*z0 + x1*y0*z1; - result.z = x0*y0*z1 - x1*y1*z0; - result.w = x0*y0*z0 + x1*y1*z1; - - return result; -} - -// Get the Euler angles equivalent to quaternion (roll, pitch, yaw) -// NOTE: Angles are returned in a Vector3 struct in radians -RMAPI Vector3 QuaternionToEuler(Quaternion q) -{ - Vector3 result = { 0 }; - - // Roll (x-axis rotation) - float x0 = 2.0f*(q.w*q.x + q.y*q.z); - float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); - result.x = atan2f(x0, x1); - - // Pitch (y-axis rotation) - float y0 = 2.0f*(q.w*q.y - q.z*q.x); - y0 = y0 > 1.0f ? 1.0f : y0; - y0 = y0 < -1.0f ? -1.0f : y0; - result.y = asinf(y0); - - // Yaw (z-axis rotation) - float z0 = 2.0f*(q.w*q.z + q.x*q.y); - float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); - result.z = atan2f(z0, z1); - - return result; -} - -// Transform a quaternion given a transformation matrix -RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat) -{ - Quaternion result = { 0 }; - - result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; - result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; - result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; - result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; - - return result; -} - -// Check whether two given quaternions are almost equal -RMAPI int QuaternionEquals(Quaternion p, Quaternion q) -{ - int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && - ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && - ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) || - (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && - ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && - ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))); - - return result; -} - -#endif // RAYMATH_H +/********************************************************************************************** +* +* raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions +* +* CONFIGURATION: +* +* #define RAYMATH_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define RAYMATH_STATIC_INLINE +* Define static inline functions code, so #include header suffices for use. +* This may use up lots of memory. +* +* CONVENTIONS: +* +* - Functions are always self-contained, no function use another raymath function inside, +* required code is directly re-implemented inside +* - Functions input parameters are always received by value (2 unavoidable exceptions) +* - Functions use always a "result" variable for return +* - Functions are always defined inline +* - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RAYMATH_H +#define RAYMATH_H + +#if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_STATIC_INLINE) + #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_STATIC_INLINE is contradictory" +#endif + +// Function specifiers definition +#if defined(RAYMATH_IMPLEMENTATION) + #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) + #define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll). + #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) + #define RMAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) + #else + #define RMAPI extern inline // Provide external definition + #endif +#elif defined(RAYMATH_STATIC_INLINE) + #define RMAPI static inline // Functions may be inlined, no external out-of-line definition +#else + #if defined(__TINYC__) + #define RMAPI static inline // plain inline not supported by tinycc (See issue #435) + #else + #define RMAPI inline // Functions may be inlined or external definition used + #endif +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef PI + #define PI 3.14159265358979323846f +#endif + +#ifndef EPSILON + #define EPSILON 0.000001f +#endif + +#ifndef DEG2RAD + #define DEG2RAD (PI/180.0f) +#endif + +#ifndef RAD2DEG + #define RAD2DEG (180.0f/PI) +#endif + +// Get float vector for Matrix +#ifndef MatrixToFloat + #define MatrixToFloat(mat) (MatrixToFloatV(mat).v) +#endif + +// Get float vector for Vector3 +#ifndef Vector3ToFloat + #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +#if !defined(RL_VECTOR2_TYPE) +// Vector2 type +typedef struct Vector2 { + float x; + float y; +} Vector2; +#define RL_VECTOR2_TYPE +#endif + +#if !defined(RL_VECTOR3_TYPE) +// Vector3 type +typedef struct Vector3 { + float x; + float y; + float z; +} Vector3; +#define RL_VECTOR3_TYPE +#endif + +#if !defined(RL_VECTOR4_TYPE) +// Vector4 type +typedef struct Vector4 { + float x; + float y; + float z; + float w; +} Vector4; +#define RL_VECTOR4_TYPE +#endif + +#if !defined(RL_QUATERNION_TYPE) +// Quaternion type +typedef Vector4 Quaternion; +#define RL_QUATERNION_TYPE +#endif + +#if !defined(RL_MATRIX_TYPE) +// Matrix type (OpenGL style 4x4 - right handed, column major) +typedef struct Matrix { + float m0, m4, m8, m12; // Matrix first row (4 components) + float m1, m5, m9, m13; // Matrix second row (4 components) + float m2, m6, m10, m14; // Matrix third row (4 components) + float m3, m7, m11, m15; // Matrix fourth row (4 components) +} Matrix; +#define RL_MATRIX_TYPE +#endif + +// NOTE: Helper types to be used instead of array return types for *ToFloat functions +typedef struct float3 { + float v[3]; +} float3; + +typedef struct float16 { + float v[16]; +} float16; + +#include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabs() + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Utils math +//---------------------------------------------------------------------------------- + +// Clamp float value +RMAPI float Clamp(float value, float min, float max) +{ + float result = (value < min)? min : value; + + if (result > max) result = max; + + return result; +} + +// Calculate linear interpolation between two floats +RMAPI float Lerp(float start, float end, float amount) +{ + float result = start + amount*(end - start); + + return result; +} + +// Normalize input value within input range +RMAPI float Normalize(float value, float start, float end) +{ + float result = (value - start)/(end - start); + + return result; +} + +// Remap input value within input range to output range +RMAPI float Remap(float value, float inputStart, float inputEnd, float outputStart, float outputEnd) +{ + float result = (value - inputStart)/(inputEnd - inputStart)*(outputEnd - outputStart) + outputStart; + + return result; +} + +// Wrap input value from min to max +RMAPI float Wrap(float value, float min, float max) +{ + float result = value - (max - min)*floorf((value - min)/(max - min)); + + return result; +} + +// Check whether two given floats are almost equal +RMAPI int FloatEquals(float x, float y) +{ + int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); + + return result; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vector2 math +//---------------------------------------------------------------------------------- + +// Vector with components value 0.0f +RMAPI Vector2 Vector2Zero(void) +{ + Vector2 result = { 0.0f, 0.0f }; + + return result; +} + +// Vector with components value 1.0f +RMAPI Vector2 Vector2One(void) +{ + Vector2 result = { 1.0f, 1.0f }; + + return result; +} + +// Add two vectors (v1 + v2) +RMAPI Vector2 Vector2Add(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x + v2.x, v1.y + v2.y }; + + return result; +} + +// Add vector and float value +RMAPI Vector2 Vector2AddValue(Vector2 v, float add) +{ + Vector2 result = { v.x + add, v.y + add }; + + return result; +} + +// Subtract two vectors (v1 - v2) +RMAPI Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x - v2.x, v1.y - v2.y }; + + return result; +} + +// Subtract vector by float value +RMAPI Vector2 Vector2SubtractValue(Vector2 v, float sub) +{ + Vector2 result = { v.x - sub, v.y - sub }; + + return result; +} + +// Calculate vector length +RMAPI float Vector2Length(Vector2 v) +{ + float result = sqrtf((v.x*v.x) + (v.y*v.y)); + + return result; +} + +// Calculate vector square length +RMAPI float Vector2LengthSqr(Vector2 v) +{ + float result = (v.x*v.x) + (v.y*v.y); + + return result; +} + +// Calculate two vectors dot product +RMAPI float Vector2DotProduct(Vector2 v1, Vector2 v2) +{ + float result = (v1.x*v2.x + v1.y*v2.y); + + return result; +} + +// Calculate distance between two vectors +RMAPI float Vector2Distance(Vector2 v1, Vector2 v2) +{ + float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); + + return result; +} + +// Calculate square distance between two vectors +RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) +{ + float result = ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); + + return result; +} + +// Calculate angle between two vectors +// NOTE: Angle is calculated from origin point (0, 0) +RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) +{ + float result = atan2f(v2.y - v1.y, v2.x - v1.x); + + return result; +} + +// Calculate angle defined by a two vectors line +// NOTE: Parameters need to be normalized +// Current implementation should be aligned with glm::angle +RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) +{ + float result = 0.0f; + + float dot = start.x*end.x + start.y*end.y; // Dot product + + float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp + if (dotClamp > 1.0f) dotClamp = 1.0f; + + result = acosf(dotClamp); + + // Alternative implementation, more costly + //float v1Length = sqrtf((start.x*start.x) + (start.y*start.y)); + //float v2Length = sqrtf((end.x*end.x) + (end.y*end.y)); + //float result = -acosf((start.x*end.x + start.y*end.y)/(v1Length*v2Length)); + + return result; +} + +// Scale vector (multiply by value) +RMAPI Vector2 Vector2Scale(Vector2 v, float scale) +{ + Vector2 result = { v.x*scale, v.y*scale }; + + return result; +} + +// Multiply vector by vector +RMAPI Vector2 Vector2Multiply(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x*v2.x, v1.y*v2.y }; + + return result; +} + +// Negate vector +RMAPI Vector2 Vector2Negate(Vector2 v) +{ + Vector2 result = { -v.x, -v.y }; + + return result; +} + +// Divide vector by vector +RMAPI Vector2 Vector2Divide(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x/v2.x, v1.y/v2.y }; + + return result; +} + +// Normalize provided vector +RMAPI Vector2 Vector2Normalize(Vector2 v) +{ + Vector2 result = { 0 }; + float length = sqrtf((v.x*v.x) + (v.y*v.y)); + + if (length > 0) + { + float ilength = 1.0f/length; + result.x = v.x*ilength; + result.y = v.y*ilength; + } + + return result; +} + +// Transforms a Vector2 by a given Matrix +RMAPI Vector2 Vector2Transform(Vector2 v, Matrix mat) +{ + Vector2 result = { 0 }; + + float x = v.x; + float y = v.y; + float z = 0; + + result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; + result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; + + return result; +} + +// Calculate linear interpolation between two vectors +RMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount) +{ + Vector2 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + + return result; +} + +// Calculate reflected vector to normal +RMAPI Vector2 Vector2Reflect(Vector2 v, Vector2 normal) +{ + Vector2 result = { 0 }; + + float dotProduct = (v.x*normal.x + v.y*normal.y); // Dot product + + result.x = v.x - (2.0f*normal.x)*dotProduct; + result.y = v.y - (2.0f*normal.y)*dotProduct; + + return result; +} + +// Rotate vector by angle +RMAPI Vector2 Vector2Rotate(Vector2 v, float angle) +{ + Vector2 result = { 0 }; + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.x = v.x*cosres - v.y*sinres; + result.y = v.x*sinres + v.y*cosres; + + return result; +} + +// Move Vector towards target +RMAPI Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance) +{ + Vector2 result = { 0 }; + + float dx = target.x - v.x; + float dy = target.y - v.y; + float value = (dx*dx) + (dy*dy); + + if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; + + float dist = sqrtf(value); + + result.x = v.x + dx/dist*maxDistance; + result.y = v.y + dy/dist*maxDistance; + + return result; +} + +// Invert the given vector +RMAPI Vector2 Vector2Invert(Vector2 v) +{ + Vector2 result = { 1.0f/v.x, 1.0f/v.y }; + + return result; +} + +// Clamp the components of the vector between +// min and max values specified by the given vectors +RMAPI Vector2 Vector2Clamp(Vector2 v, Vector2 min, Vector2 max) +{ + Vector2 result = { 0 }; + + result.x = fminf(max.x, fmaxf(min.x, v.x)); + result.y = fminf(max.y, fmaxf(min.y, v.y)); + + return result; +} + +// Clamp the magnitude of the vector between two min and max values +RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max) +{ + Vector2 result = v; + + float length = (v.x*v.x) + (v.y*v.y); + if (length > 0.0f) + { + length = sqrtf(length); + + if (length < min) + { + float scale = min/length; + result.x = v.x*scale; + result.y = v.y*scale; + } + else if (length > max) + { + float scale = max/length; + result.x = v.x*scale; + result.y = v.y*scale; + } + } + + return result; +} + +// Check whether two given vectors are almost equal +RMAPI int Vector2Equals(Vector2 p, Vector2 q) +{ + int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))); + + return result; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vector3 math +//---------------------------------------------------------------------------------- + +// Vector with components value 0.0f +RMAPI Vector3 Vector3Zero(void) +{ + Vector3 result = { 0.0f, 0.0f, 0.0f }; + + return result; +} + +// Vector with components value 1.0f +RMAPI Vector3 Vector3One(void) +{ + Vector3 result = { 1.0f, 1.0f, 1.0f }; + + return result; +} + +// Add two vectors +RMAPI Vector3 Vector3Add(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; + + return result; +} + +// Add vector and float value +RMAPI Vector3 Vector3AddValue(Vector3 v, float add) +{ + Vector3 result = { v.x + add, v.y + add, v.z + add }; + + return result; +} + +// Subtract two vectors +RMAPI Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; + + return result; +} + +// Subtract vector by float value +RMAPI Vector3 Vector3SubtractValue(Vector3 v, float sub) +{ + Vector3 result = { v.x - sub, v.y - sub, v.z - sub }; + + return result; +} + +// Multiply vector by scalar +RMAPI Vector3 Vector3Scale(Vector3 v, float scalar) +{ + Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; + + return result; +} + +// Multiply vector by vector +RMAPI Vector3 Vector3Multiply(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; + + return result; +} + +// Calculate two vectors cross product +RMAPI Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; + + return result; +} + +// Calculate one vector perpendicular vector +RMAPI Vector3 Vector3Perpendicular(Vector3 v) +{ + Vector3 result = { 0 }; + + float min = (float) fabs(v.x); + Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; + + if (fabsf(v.y) < min) + { + min = (float) fabs(v.y); + Vector3 tmp = {0.0f, 1.0f, 0.0f}; + cardinalAxis = tmp; + } + + if (fabsf(v.z) < min) + { + Vector3 tmp = {0.0f, 0.0f, 1.0f}; + cardinalAxis = tmp; + } + + // Cross product between vectors + result.x = v.y*cardinalAxis.z - v.z*cardinalAxis.y; + result.y = v.z*cardinalAxis.x - v.x*cardinalAxis.z; + result.z = v.x*cardinalAxis.y - v.y*cardinalAxis.x; + + return result; +} + +// Calculate vector length +RMAPI float Vector3Length(const Vector3 v) +{ + float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + + return result; +} + +// Calculate vector square length +RMAPI float Vector3LengthSqr(const Vector3 v) +{ + float result = v.x*v.x + v.y*v.y + v.z*v.z; + + return result; +} + +// Calculate two vectors dot product +RMAPI float Vector3DotProduct(Vector3 v1, Vector3 v2) +{ + float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + + return result; +} + +// Calculate distance between two vectors +RMAPI float Vector3Distance(Vector3 v1, Vector3 v2) +{ + float result = 0.0f; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + float dz = v2.z - v1.z; + result = sqrtf(dx*dx + dy*dy + dz*dz); + + return result; +} + +// Calculate square distance between two vectors +RMAPI float Vector3DistanceSqr(Vector3 v1, Vector3 v2) +{ + float result = 0.0f; + + float dx = v2.x - v1.x; + float dy = v2.y - v1.y; + float dz = v2.z - v1.z; + result = dx*dx + dy*dy + dz*dz; + + return result; +} + +// Calculate angle between two vectors +RMAPI float Vector3Angle(Vector3 v1, Vector3 v2) +{ + float result = 0.0f; + + Vector3 cross = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; + float len = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z); + float dot = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + result = atan2f(len, dot); + + return result; +} + +// Negate provided vector (invert direction) +RMAPI Vector3 Vector3Negate(Vector3 v) +{ + Vector3 result = { -v.x, -v.y, -v.z }; + + return result; +} + +// Divide vector by vector +RMAPI Vector3 Vector3Divide(Vector3 v1, Vector3 v2) +{ + Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; + + return result; +} + +// Normalize provided vector +RMAPI Vector3 Vector3Normalize(Vector3 v) +{ + Vector3 result = v; + + float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x *= ilength; + result.y *= ilength; + result.z *= ilength; + + return result; +} + +// Orthonormalize provided vectors +// Makes vectors normalized and orthogonal to each other +// Gram-Schmidt function implementation +RMAPI void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2) +{ + float length = 0.0f; + float ilength = 0.0f; + + // Vector3Normalize(*v1); + Vector3 v = *v1; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + v1->x *= ilength; + v1->y *= ilength; + v1->z *= ilength; + + // Vector3CrossProduct(*v1, *v2) + Vector3 vn1 = { v1->y*v2->z - v1->z*v2->y, v1->z*v2->x - v1->x*v2->z, v1->x*v2->y - v1->y*v2->x }; + + // Vector3Normalize(vn1); + v = vn1; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + vn1.x *= ilength; + vn1.y *= ilength; + vn1.z *= ilength; + + // Vector3CrossProduct(vn1, *v1) + Vector3 vn2 = { vn1.y*v1->z - vn1.z*v1->y, vn1.z*v1->x - vn1.x*v1->z, vn1.x*v1->y - vn1.y*v1->x }; + + *v2 = vn2; +} + +// Transforms a Vector3 by a given Matrix +RMAPI Vector3 Vector3Transform(Vector3 v, Matrix mat) +{ + Vector3 result = { 0 }; + + float x = v.x; + float y = v.y; + float z = v.z; + + result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; + result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; + result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; + + return result; +} + +// Transform a vector by quaternion rotation +RMAPI Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) +{ + Vector3 result = { 0 }; + + result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); + result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); + result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); + + return result; +} + +// Rotates a vector around an axis +RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) +{ + // Using Euler-Rodrigues Formula + // Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula + + Vector3 result = v; + + // Vector3Normalize(axis); + float length = sqrtf(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f / length; + axis.x *= ilength; + axis.y *= ilength; + axis.z *= ilength; + + angle /= 2.0f; + float a = sinf(angle); + float b = axis.x * a; + float c = axis.y * a; + float d = axis.z * a; + a = cosf(angle); + Vector3 w = { b, c, d }; + + // Vector3CrossProduct(w, v) + Vector3 wv = { w.y * v.z - w.z * v.y, w.z * v.x - w.x * v.z, w.x * v.y - w.y * v.x }; + + // Vector3CrossProduct(w, wv) + Vector3 wwv = { w.y * wv.z - w.z * wv.y, w.z * wv.x - w.x * wv.z, w.x * wv.y - w.y * wv.x }; + + // Vector3Scale(wv, 2 * a) + a *= 2; + wv.x *= a; + wv.y *= a; + wv.z *= a; + + // Vector3Scale(wwv, 2) + wwv.x *= 2; + wwv.y *= 2; + wwv.z *= 2; + + result.x += wv.x; + result.y += wv.y; + result.z += wv.z; + + result.x += wwv.x; + result.y += wwv.y; + result.z += wwv.z; + + return result; +} + +// Calculate linear interpolation between two vectors +RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) +{ + Vector3 result = { 0 }; + + result.x = v1.x + amount*(v2.x - v1.x); + result.y = v1.y + amount*(v2.y - v1.y); + result.z = v1.z + amount*(v2.z - v1.z); + + return result; +} + +// Calculate reflected vector to normal +RMAPI Vector3 Vector3Reflect(Vector3 v, Vector3 normal) +{ + Vector3 result = { 0 }; + + // I is the original vector + // N is the normal of the incident plane + // R = I - (2*N*(DotProduct[I, N])) + + float dotProduct = (v.x*normal.x + v.y*normal.y + v.z*normal.z); + + result.x = v.x - (2.0f*normal.x)*dotProduct; + result.y = v.y - (2.0f*normal.y)*dotProduct; + result.z = v.z - (2.0f*normal.z)*dotProduct; + + return result; +} + +// Get min value for each pair of components +RMAPI Vector3 Vector3Min(Vector3 v1, Vector3 v2) +{ + Vector3 result = { 0 }; + + result.x = fminf(v1.x, v2.x); + result.y = fminf(v1.y, v2.y); + result.z = fminf(v1.z, v2.z); + + return result; +} + +// Get max value for each pair of components +RMAPI Vector3 Vector3Max(Vector3 v1, Vector3 v2) +{ + Vector3 result = { 0 }; + + result.x = fmaxf(v1.x, v2.x); + result.y = fmaxf(v1.y, v2.y); + result.z = fmaxf(v1.z, v2.z); + + return result; +} + +// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) +// NOTE: Assumes P is on the plane of the triangle +RMAPI Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) +{ + Vector3 result = { 0 }; + + Vector3 v0 = { b.x - a.x, b.y - a.y, b.z - a.z }; // Vector3Subtract(b, a) + Vector3 v1 = { c.x - a.x, c.y - a.y, c.z - a.z }; // Vector3Subtract(c, a) + Vector3 v2 = { p.x - a.x, p.y - a.y, p.z - a.z }; // Vector3Subtract(p, a) + float d00 = (v0.x*v0.x + v0.y*v0.y + v0.z*v0.z); // Vector3DotProduct(v0, v0) + float d01 = (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z); // Vector3DotProduct(v0, v1) + float d11 = (v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); // Vector3DotProduct(v1, v1) + float d20 = (v2.x*v0.x + v2.y*v0.y + v2.z*v0.z); // Vector3DotProduct(v2, v0) + float d21 = (v2.x*v1.x + v2.y*v1.y + v2.z*v1.z); // Vector3DotProduct(v2, v1) + + float denom = d00*d11 - d01*d01; + + result.y = (d11*d20 - d01*d21)/denom; + result.z = (d00*d21 - d01*d20)/denom; + result.x = 1.0f - (result.z + result.y); + + return result; +} + +// Projects a Vector3 from screen space into object space +// NOTE: We are avoiding calling other raymath functions despite available +RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) +{ + Vector3 result = { 0 }; + + // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it + Matrix matViewProj = { // MatrixMultiply(view, projection); + view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, + view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, + view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14, + view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15, + view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12, + view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13, + view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14, + view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15, + view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12, + view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13, + view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14, + view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15, + view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12, + view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13, + view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14, + view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 }; + + // Calculate inverted matrix -> MatrixInvert(matViewProj); + // Cache the matrix values (speed optimization) + float a00 = matViewProj.m0, a01 = matViewProj.m1, a02 = matViewProj.m2, a03 = matViewProj.m3; + float a10 = matViewProj.m4, a11 = matViewProj.m5, a12 = matViewProj.m6, a13 = matViewProj.m7; + float a20 = matViewProj.m8, a21 = matViewProj.m9, a22 = matViewProj.m10, a23 = matViewProj.m11; + float a30 = matViewProj.m12, a31 = matViewProj.m13, a32 = matViewProj.m14, a33 = matViewProj.m15; + + float b00 = a00*a11 - a01*a10; + float b01 = a00*a12 - a02*a10; + float b02 = a00*a13 - a03*a10; + float b03 = a01*a12 - a02*a11; + float b04 = a01*a13 - a03*a11; + float b05 = a02*a13 - a03*a12; + float b06 = a20*a31 - a21*a30; + float b07 = a20*a32 - a22*a30; + float b08 = a20*a33 - a23*a30; + float b09 = a21*a32 - a22*a31; + float b10 = a21*a33 - a23*a31; + float b11 = a22*a33 - a23*a32; + + // Calculate the invert determinant (inlined to avoid double-caching) + float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); + + Matrix matViewProjInv = { + (a11*b11 - a12*b10 + a13*b09)*invDet, + (-a01*b11 + a02*b10 - a03*b09)*invDet, + (a31*b05 - a32*b04 + a33*b03)*invDet, + (-a21*b05 + a22*b04 - a23*b03)*invDet, + (-a10*b11 + a12*b08 - a13*b07)*invDet, + (a00*b11 - a02*b08 + a03*b07)*invDet, + (-a30*b05 + a32*b02 - a33*b01)*invDet, + (a20*b05 - a22*b02 + a23*b01)*invDet, + (a10*b10 - a11*b08 + a13*b06)*invDet, + (-a00*b10 + a01*b08 - a03*b06)*invDet, + (a30*b04 - a31*b02 + a33*b00)*invDet, + (-a20*b04 + a21*b02 - a23*b00)*invDet, + (-a10*b09 + a11*b07 - a12*b06)*invDet, + (a00*b09 - a01*b07 + a02*b06)*invDet, + (-a30*b03 + a31*b01 - a32*b00)*invDet, + (a20*b03 - a21*b01 + a22*b00)*invDet }; + + // Create quaternion from source point + Quaternion quat = { source.x, source.y, source.z, 1.0f }; + + // Multiply quat point by unprojecte matrix + Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) + matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, + matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, + matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w, + matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w }; + + // Normalized world points in vectors + result.x = qtransformed.x/qtransformed.w; + result.y = qtransformed.y/qtransformed.w; + result.z = qtransformed.z/qtransformed.w; + + return result; +} + +// Get Vector3 as float array +RMAPI float3 Vector3ToFloatV(Vector3 v) +{ + float3 buffer = { 0 }; + + buffer.v[0] = v.x; + buffer.v[1] = v.y; + buffer.v[2] = v.z; + + return buffer; +} + +// Invert the given vector +RMAPI Vector3 Vector3Invert(Vector3 v) +{ + Vector3 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z }; + + return result; +} + +// Clamp the components of the vector between +// min and max values specified by the given vectors +RMAPI Vector3 Vector3Clamp(Vector3 v, Vector3 min, Vector3 max) +{ + Vector3 result = { 0 }; + + result.x = fminf(max.x, fmaxf(min.x, v.x)); + result.y = fminf(max.y, fmaxf(min.y, v.y)); + result.z = fminf(max.z, fmaxf(min.z, v.z)); + + return result; +} + +// Clamp the magnitude of the vector between two values +RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) +{ + Vector3 result = v; + + float length = (v.x*v.x) + (v.y*v.y) + (v.z*v.z); + if (length > 0.0f) + { + length = sqrtf(length); + + if (length < min) + { + float scale = min/length; + result.x = v.x*scale; + result.y = v.y*scale; + result.z = v.z*scale; + } + else if (length > max) + { + float scale = max/length; + result.x = v.x*scale; + result.y = v.y*scale; + result.z = v.z*scale; + } + } + + return result; +} + +// Check whether two given vectors are almost equal +RMAPI int Vector3Equals(Vector3 p, Vector3 q) +{ + int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); + + return result; +} + +// Compute the direction of a refracted ray where v specifies the +// normalized direction of the incoming ray, n specifies the +// normalized normal vector of the interface of two optical media, +// and r specifies the ratio of the refractive index of the medium +// from where the ray comes to the refractive index of the medium +// on the other side of the surface +RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) +{ + Vector3 result = { 0 }; + + float dot = v.x*n.x + v.y*n.y + v.z*n.z; + float d = 1.0f - r*r*(1.0f - dot*dot); + + if (d >= 0.0f) + { + d = sqrtf(d); + v.x = r*v.x - (r*dot + d)*n.x; + v.y = r*v.y - (r*dot + d)*n.y; + v.z = r*v.z - (r*dot + d)*n.z; + + result = v; + } + + return result; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Matrix math +//---------------------------------------------------------------------------------- + +// Compute matrix determinant +RMAPI float MatrixDeterminant(Matrix mat) +{ + float result = 0.0f; + + // Cache the matrix values (speed optimization) + float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; + float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; + float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; + float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; + + result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + + a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + + a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + + a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + + a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + + a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; + + return result; +} + +// Get the trace of the matrix (sum of the values along the diagonal) +RMAPI float MatrixTrace(Matrix mat) +{ + float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); + + return result; +} + +// Transposes provided matrix +RMAPI Matrix MatrixTranspose(Matrix mat) +{ + Matrix result = { 0 }; + + result.m0 = mat.m0; + result.m1 = mat.m4; + result.m2 = mat.m8; + result.m3 = mat.m12; + result.m4 = mat.m1; + result.m5 = mat.m5; + result.m6 = mat.m9; + result.m7 = mat.m13; + result.m8 = mat.m2; + result.m9 = mat.m6; + result.m10 = mat.m10; + result.m11 = mat.m14; + result.m12 = mat.m3; + result.m13 = mat.m7; + result.m14 = mat.m11; + result.m15 = mat.m15; + + return result; +} + +// Invert provided matrix +RMAPI Matrix MatrixInvert(Matrix mat) +{ + Matrix result = { 0 }; + + // Cache the matrix values (speed optimization) + float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; + float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; + float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; + float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; + + float b00 = a00*a11 - a01*a10; + float b01 = a00*a12 - a02*a10; + float b02 = a00*a13 - a03*a10; + float b03 = a01*a12 - a02*a11; + float b04 = a01*a13 - a03*a11; + float b05 = a02*a13 - a03*a12; + float b06 = a20*a31 - a21*a30; + float b07 = a20*a32 - a22*a30; + float b08 = a20*a33 - a23*a30; + float b09 = a21*a32 - a22*a31; + float b10 = a21*a33 - a23*a31; + float b11 = a22*a33 - a23*a32; + + // Calculate the invert determinant (inlined to avoid double-caching) + float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); + + result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; + result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; + result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; + result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; + result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; + result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; + result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; + result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; + result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; + result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; + result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; + result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; + result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; + result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; + result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; + result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; + + return result; +} + +// Get identity matrix +RMAPI Matrix MatrixIdentity(void) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Add two matrices +RMAPI Matrix MatrixAdd(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0 + right.m0; + result.m1 = left.m1 + right.m1; + result.m2 = left.m2 + right.m2; + result.m3 = left.m3 + right.m3; + result.m4 = left.m4 + right.m4; + result.m5 = left.m5 + right.m5; + result.m6 = left.m6 + right.m6; + result.m7 = left.m7 + right.m7; + result.m8 = left.m8 + right.m8; + result.m9 = left.m9 + right.m9; + result.m10 = left.m10 + right.m10; + result.m11 = left.m11 + right.m11; + result.m12 = left.m12 + right.m12; + result.m13 = left.m13 + right.m13; + result.m14 = left.m14 + right.m14; + result.m15 = left.m15 + right.m15; + + return result; +} + +// Subtract two matrices (left - right) +RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0 - right.m0; + result.m1 = left.m1 - right.m1; + result.m2 = left.m2 - right.m2; + result.m3 = left.m3 - right.m3; + result.m4 = left.m4 - right.m4; + result.m5 = left.m5 - right.m5; + result.m6 = left.m6 - right.m6; + result.m7 = left.m7 - right.m7; + result.m8 = left.m8 - right.m8; + result.m9 = left.m9 - right.m9; + result.m10 = left.m10 - right.m10; + result.m11 = left.m11 - right.m11; + result.m12 = left.m12 - right.m12; + result.m13 = left.m13 - right.m13; + result.m14 = left.m14 - right.m14; + result.m15 = left.m15 - right.m15; + + return result; +} + +// Get two matrix multiplication +// NOTE: When multiplying matrices... the order matters! +RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; + result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; + result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; + result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; + result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; + result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; + result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; + result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; + result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; + result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; + result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; + result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; + result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; + result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; + result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; + result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; + + return result; +} + +// Get translation matrix +RMAPI Matrix MatrixTranslate(float x, float y, float z) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, x, + 0.0f, 1.0f, 0.0f, y, + 0.0f, 0.0f, 1.0f, z, + 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Create rotation matrix from axis and angle +// NOTE: Angle should be provided in radians +RMAPI Matrix MatrixRotate(Vector3 axis, float angle) +{ + Matrix result = { 0 }; + + float x = axis.x, y = axis.y, z = axis.z; + + float lengthSquared = x*x + y*y + z*z; + + if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) + { + float ilength = 1.0f/sqrtf(lengthSquared); + x *= ilength; + y *= ilength; + z *= ilength; + } + + float sinres = sinf(angle); + float cosres = cosf(angle); + float t = 1.0f - cosres; + + result.m0 = x*x*t + cosres; + result.m1 = y*x*t + z*sinres; + result.m2 = z*x*t - y*sinres; + result.m3 = 0.0f; + + result.m4 = x*y*t - z*sinres; + result.m5 = y*y*t + cosres; + result.m6 = z*y*t + x*sinres; + result.m7 = 0.0f; + + result.m8 = x*z*t + y*sinres; + result.m9 = y*z*t - x*sinres; + result.m10 = z*z*t + cosres; + result.m11 = 0.0f; + + result.m12 = 0.0f; + result.m13 = 0.0f; + result.m14 = 0.0f; + result.m15 = 1.0f; + + return result; +} + +// Get x-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateX(float angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.m5 = cosres; + result.m6 = sinres; + result.m9 = -sinres; + result.m10 = cosres; + + return result; +} + +// Get y-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateY(float angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.m0 = cosres; + result.m2 = -sinres; + result.m8 = sinres; + result.m10 = cosres; + + return result; +} + +// Get z-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateZ(float angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosres = cosf(angle); + float sinres = sinf(angle); + + result.m0 = cosres; + result.m1 = sinres; + result.m4 = -sinres; + result.m5 = cosres; + + return result; +} + + +// Get xyz-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateXYZ(Vector3 angle) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float cosz = cosf(-angle.z); + float sinz = sinf(-angle.z); + float cosy = cosf(-angle.y); + float siny = sinf(-angle.y); + float cosx = cosf(-angle.x); + float sinx = sinf(-angle.x); + + result.m0 = cosz*cosy; + result.m1 = (cosz*siny*sinx) - (sinz*cosx); + result.m2 = (cosz*siny*cosx) + (sinz*sinx); + + result.m4 = sinz*cosy; + result.m5 = (sinz*siny*sinx) + (cosz*cosx); + result.m6 = (sinz*siny*cosx) - (cosz*sinx); + + result.m8 = -siny; + result.m9 = cosy*sinx; + result.m10= cosy*cosx; + + return result; +} + +// Get zyx-rotation matrix +// NOTE: Angle must be provided in radians +RMAPI Matrix MatrixRotateZYX(Vector3 angle) +{ + Matrix result = { 0 }; + + float cz = cosf(angle.z); + float sz = sinf(angle.z); + float cy = cosf(angle.y); + float sy = sinf(angle.y); + float cx = cosf(angle.x); + float sx = sinf(angle.x); + + result.m0 = cz*cy; + result.m4 = cz*sy*sx - cx*sz; + result.m8 = sz*sx + cz*cx*sy; + result.m12 = 0; + + result.m1 = cy*sz; + result.m5 = cz*cx + sz*sy*sx; + result.m9 = cx*sz*sy - cz*sx; + result.m13 = 0; + + result.m2 = -sy; + result.m6 = cy*sx; + result.m10 = cy*cx; + result.m14 = 0; + + result.m3 = 0; + result.m7 = 0; + result.m11 = 0; + result.m15 = 1; + + return result; +} + +// Get scaling matrix +RMAPI Matrix MatrixScale(float x, float y, float z) +{ + Matrix result = { x, 0.0f, 0.0f, 0.0f, + 0.0f, y, 0.0f, 0.0f, + 0.0f, 0.0f, z, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Get perspective projection matrix +RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far) +{ + Matrix result = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(far - near); + + result.m0 = ((float)near*2.0f)/rl; + result.m1 = 0.0f; + result.m2 = 0.0f; + result.m3 = 0.0f; + + result.m4 = 0.0f; + result.m5 = ((float)near*2.0f)/tb; + result.m6 = 0.0f; + result.m7 = 0.0f; + + result.m8 = ((float)right + (float)left)/rl; + result.m9 = ((float)top + (float)bottom)/tb; + result.m10 = -((float)far + (float)near)/fn; + result.m11 = -1.0f; + + result.m12 = 0.0f; + result.m13 = 0.0f; + result.m14 = -((float)far*(float)near*2.0f)/fn; + result.m15 = 0.0f; + + return result; +} + +// Get perspective projection matrix +// NOTE: Fovy angle must be provided in radians +RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double far) +{ + Matrix result = { 0 }; + + double top = near*tan(fovy*0.5); + double bottom = -top; + double right = top*aspect; + double left = -right; + + // MatrixFrustum(-right, right, -top, top, near, far); + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(far - near); + + result.m0 = ((float)near*2.0f)/rl; + result.m5 = ((float)near*2.0f)/tb; + result.m8 = ((float)right + (float)left)/rl; + result.m9 = ((float)top + (float)bottom)/tb; + result.m10 = -((float)far + (float)near)/fn; + result.m11 = -1.0f; + result.m14 = -((float)far*(float)near*2.0f)/fn; + + return result; +} + +// Get orthographic projection matrix +RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far) +{ + Matrix result = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(far - near); + + result.m0 = 2.0f/rl; + result.m1 = 0.0f; + result.m2 = 0.0f; + result.m3 = 0.0f; + result.m4 = 0.0f; + result.m5 = 2.0f/tb; + result.m6 = 0.0f; + result.m7 = 0.0f; + result.m8 = 0.0f; + result.m9 = 0.0f; + result.m10 = -2.0f/fn; + result.m11 = 0.0f; + result.m12 = -((float)left + (float)right)/rl; + result.m13 = -((float)top + (float)bottom)/tb; + result.m14 = -((float)far + (float)near)/fn; + result.m15 = 1.0f; + + return result; +} + +// Get camera look-at matrix (view matrix) +RMAPI Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) +{ + Matrix result = { 0 }; + + float length = 0.0f; + float ilength = 0.0f; + + // Vector3Subtract(eye, target) + Vector3 vz = { eye.x - target.x, eye.y - target.y, eye.z - target.z }; + + // Vector3Normalize(vz) + Vector3 v = vz; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + vz.x *= ilength; + vz.y *= ilength; + vz.z *= ilength; + + // Vector3CrossProduct(up, vz) + Vector3 vx = { up.y*vz.z - up.z*vz.y, up.z*vz.x - up.x*vz.z, up.x*vz.y - up.y*vz.x }; + + // Vector3Normalize(x) + v = vx; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + vx.x *= ilength; + vx.y *= ilength; + vx.z *= ilength; + + // Vector3CrossProduct(vz, vx) + Vector3 vy = { vz.y*vx.z - vz.z*vx.y, vz.z*vx.x - vz.x*vx.z, vz.x*vx.y - vz.y*vx.x }; + + result.m0 = vx.x; + result.m1 = vy.x; + result.m2 = vz.x; + result.m3 = 0.0f; + result.m4 = vx.y; + result.m5 = vy.y; + result.m6 = vz.y; + result.m7 = 0.0f; + result.m8 = vx.z; + result.m9 = vy.z; + result.m10 = vz.z; + result.m11 = 0.0f; + result.m12 = -(vx.x*eye.x + vx.y*eye.y + vx.z*eye.z); // Vector3DotProduct(vx, eye) + result.m13 = -(vy.x*eye.x + vy.y*eye.y + vy.z*eye.z); // Vector3DotProduct(vy, eye) + result.m14 = -(vz.x*eye.x + vz.y*eye.y + vz.z*eye.z); // Vector3DotProduct(vz, eye) + result.m15 = 1.0f; + + return result; +} + +// Get float array of matrix data +RMAPI float16 MatrixToFloatV(Matrix mat) +{ + float16 result = { 0 }; + + result.v[0] = mat.m0; + result.v[1] = mat.m1; + result.v[2] = mat.m2; + result.v[3] = mat.m3; + result.v[4] = mat.m4; + result.v[5] = mat.m5; + result.v[6] = mat.m6; + result.v[7] = mat.m7; + result.v[8] = mat.m8; + result.v[9] = mat.m9; + result.v[10] = mat.m10; + result.v[11] = mat.m11; + result.v[12] = mat.m12; + result.v[13] = mat.m13; + result.v[14] = mat.m14; + result.v[15] = mat.m15; + + return result; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Quaternion math +//---------------------------------------------------------------------------------- + +// Add two quaternions +RMAPI Quaternion QuaternionAdd(Quaternion q1, Quaternion q2) +{ + Quaternion result = {q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w}; + + return result; +} + +// Add quaternion and float value +RMAPI Quaternion QuaternionAddValue(Quaternion q, float add) +{ + Quaternion result = {q.x + add, q.y + add, q.z + add, q.w + add}; + + return result; +} + +// Subtract two quaternions +RMAPI Quaternion QuaternionSubtract(Quaternion q1, Quaternion q2) +{ + Quaternion result = {q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w}; + + return result; +} + +// Subtract quaternion and float value +RMAPI Quaternion QuaternionSubtractValue(Quaternion q, float sub) +{ + Quaternion result = {q.x - sub, q.y - sub, q.z - sub, q.w - sub}; + + return result; +} + +// Get identity quaternion +RMAPI Quaternion QuaternionIdentity(void) +{ + Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +// Computes the length of a quaternion +RMAPI float QuaternionLength(Quaternion q) +{ + float result = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + + return result; +} + +// Normalize provided quaternion +RMAPI Quaternion QuaternionNormalize(Quaternion q) +{ + Quaternion result = { 0 }; + + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + + return result; +} + +// Invert provided quaternion +RMAPI Quaternion QuaternionInvert(Quaternion q) +{ + Quaternion result = q; + + float lengthSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; + + if (lengthSq != 0.0f) + { + float invLength = 1.0f/lengthSq; + + result.x *= -invLength; + result.y *= -invLength; + result.z *= -invLength; + result.w *= invLength; + } + + return result; +} + +// Calculate two quaternion multiplication +RMAPI Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) +{ + Quaternion result = { 0 }; + + float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; + float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; + + result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; + result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; + result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; + result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; + + return result; +} + +// Scale quaternion by float value +RMAPI Quaternion QuaternionScale(Quaternion q, float mul) +{ + Quaternion result = { 0 }; + + result.x = q.x*mul; + result.y = q.y*mul; + result.z = q.z*mul; + result.w = q.w*mul; + + return result; +} + +// Divide two quaternions +RMAPI Quaternion QuaternionDivide(Quaternion q1, Quaternion q2) +{ + Quaternion result = { q1.x/q2.x, q1.y/q2.y, q1.z/q2.z, q1.w/q2.w }; + + return result; +} + +// Calculate linear interpolation between two quaternions +RMAPI Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = { 0 }; + + result.x = q1.x + amount*(q2.x - q1.x); + result.y = q1.y + amount*(q2.y - q1.y); + result.z = q1.z + amount*(q2.z - q1.z); + result.w = q1.w + amount*(q2.w - q1.w); + + return result; +} + +// Calculate slerp-optimized interpolation between two quaternions +RMAPI Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = { 0 }; + + // QuaternionLerp(q1, q2, amount) + result.x = q1.x + amount*(q2.x - q1.x); + result.y = q1.y + amount*(q2.y - q1.y); + result.z = q1.z + amount*(q2.z - q1.z); + result.w = q1.w + amount*(q2.w - q1.w); + + // QuaternionNormalize(q); + Quaternion q = result; + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + + return result; +} + +// Calculates spherical linear interpolation between two quaternions +RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = { 0 }; + + float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; + + if (cosHalfTheta < 0) + { + q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w; + cosHalfTheta = -cosHalfTheta; + } + + if (fabsf(cosHalfTheta) >= 1.0f) result = q1; + else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); + else + { + float halfTheta = acosf(cosHalfTheta); + float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); + + if (fabsf(sinHalfTheta) < 0.001f) + { + result.x = (q1.x*0.5f + q2.x*0.5f); + result.y = (q1.y*0.5f + q2.y*0.5f); + result.z = (q1.z*0.5f + q2.z*0.5f); + result.w = (q1.w*0.5f + q2.w*0.5f); + } + else + { + float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; + float ratioB = sinf(amount*halfTheta)/sinHalfTheta; + + result.x = (q1.x*ratioA + q2.x*ratioB); + result.y = (q1.y*ratioA + q2.y*ratioB); + result.z = (q1.z*ratioA + q2.z*ratioB); + result.w = (q1.w*ratioA + q2.w*ratioB); + } + } + + return result; +} + +// Calculate quaternion based on the rotation from one vector to another +RMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) +{ + Quaternion result = { 0 }; + + float cos2Theta = (from.x*to.x + from.y*to.y + from.z*to.z); // Vector3DotProduct(from, to) + Vector3 cross = { from.y*to.z - from.z*to.y, from.z*to.x - from.x*to.z, from.x*to.y - from.y*to.x }; // Vector3CrossProduct(from, to) + + result.x = cross.x; + result.y = cross.y; + result.z = cross.z; + result.w = 1.0f + cos2Theta; + + // QuaternionNormalize(q); + // NOTE: Normalize to essentially nlerp the original and identity to 0.5 + Quaternion q = result; + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + + return result; +} + +// Get a quaternion for a given rotation matrix +RMAPI Quaternion QuaternionFromMatrix(Matrix mat) +{ + Quaternion result = { 0 }; + + float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; + float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; + float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; + float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5; + + int biggestIndex = 0; + float fourBiggestSquaredMinus1 = fourWSquaredMinus1; + if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourXSquaredMinus1; + biggestIndex = 1; + } + + if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourYSquaredMinus1; + biggestIndex = 2; + } + + if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourZSquaredMinus1; + biggestIndex = 3; + } + + float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f) * 0.5f; + float mult = 0.25f / biggestVal; + + switch (biggestIndex) + { + case 0: + result.w = biggestVal; + result.x = (mat.m6 - mat.m9) * mult; + result.y = (mat.m8 - mat.m2) * mult; + result.z = (mat.m1 - mat.m4) * mult; + break; + case 1: + result.x = biggestVal; + result.w = (mat.m6 - mat.m9) * mult; + result.y = (mat.m1 + mat.m4) * mult; + result.z = (mat.m8 + mat.m2) * mult; + break; + case 2: + result.y = biggestVal; + result.w = (mat.m8 - mat.m2) * mult; + result.x = (mat.m1 + mat.m4) * mult; + result.z = (mat.m6 + mat.m9) * mult; + break; + case 3: + result.z = biggestVal; + result.w = (mat.m1 - mat.m4) * mult; + result.x = (mat.m8 + mat.m2) * mult; + result.y = (mat.m6 + mat.m9) * mult; + break; + } + + return result; +} + +// Get a matrix for a given quaternion +RMAPI Matrix QuaternionToMatrix(Quaternion q) +{ + Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() + + float a2 = q.x*q.x; + float b2 = q.y*q.y; + float c2 = q.z*q.z; + float ac = q.x*q.z; + float ab = q.x*q.y; + float bc = q.y*q.z; + float ad = q.w*q.x; + float bd = q.w*q.y; + float cd = q.w*q.z; + + result.m0 = 1 - 2*(b2 + c2); + result.m1 = 2*(ab + cd); + result.m2 = 2*(ac - bd); + + result.m4 = 2*(ab - cd); + result.m5 = 1 - 2*(a2 + c2); + result.m6 = 2*(bc + ad); + + result.m8 = 2*(ac + bd); + result.m9 = 2*(bc - ad); + result.m10 = 1 - 2*(a2 + b2); + + return result; +} + +// Get rotation quaternion for an angle and axis +// NOTE: Angle must be provided in radians +RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) +{ + Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; + + float axisLength = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); + + if (axisLength != 0.0f) + { + angle *= 0.5f; + + float length = 0.0f; + float ilength = 0.0f; + + // Vector3Normalize(axis) + Vector3 v = axis; + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + axis.x *= ilength; + axis.y *= ilength; + axis.z *= ilength; + + float sinres = sinf(angle); + float cosres = cosf(angle); + + result.x = axis.x*sinres; + result.y = axis.y*sinres; + result.z = axis.z*sinres; + result.w = cosres; + + // QuaternionNormalize(q); + Quaternion q = result; + length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + ilength = 1.0f/length; + result.x = q.x*ilength; + result.y = q.y*ilength; + result.z = q.z*ilength; + result.w = q.w*ilength; + } + + return result; +} + +// Get the rotation angle and axis for a given quaternion +RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) +{ + if (fabsf(q.w) > 1.0f) + { + // QuaternionNormalize(q); + float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); + if (length == 0.0f) length = 1.0f; + float ilength = 1.0f/length; + + q.x = q.x*ilength; + q.y = q.y*ilength; + q.z = q.z*ilength; + q.w = q.w*ilength; + } + + Vector3 resAxis = { 0.0f, 0.0f, 0.0f }; + float resAngle = 2.0f*acosf(q.w); + float den = sqrtf(1.0f - q.w*q.w); + + if (den > 0.0001f) + { + resAxis.x = q.x/den; + resAxis.y = q.y/den; + resAxis.z = q.z/den; + } + else + { + // This occurs when the angle is zero. + // Not a problem: just set an arbitrary normalized axis. + resAxis.x = 1.0f; + } + + *outAxis = resAxis; + *outAngle = resAngle; +} + +// Get the quaternion equivalent to Euler angles +// NOTE: Rotation order is ZYX +RMAPI Quaternion QuaternionFromEuler(float pitch, float yaw, float roll) +{ + Quaternion result = { 0 }; + + float x0 = cosf(pitch*0.5f); + float x1 = sinf(pitch*0.5f); + float y0 = cosf(yaw*0.5f); + float y1 = sinf(yaw*0.5f); + float z0 = cosf(roll*0.5f); + float z1 = sinf(roll*0.5f); + + result.x = x1*y0*z0 - x0*y1*z1; + result.y = x0*y1*z0 + x1*y0*z1; + result.z = x0*y0*z1 - x1*y1*z0; + result.w = x0*y0*z0 + x1*y1*z1; + + return result; +} + +// Get the Euler angles equivalent to quaternion (roll, pitch, yaw) +// NOTE: Angles are returned in a Vector3 struct in radians +RMAPI Vector3 QuaternionToEuler(Quaternion q) +{ + Vector3 result = { 0 }; + + // Roll (x-axis rotation) + float x0 = 2.0f*(q.w*q.x + q.y*q.z); + float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); + result.x = atan2f(x0, x1); + + // Pitch (y-axis rotation) + float y0 = 2.0f*(q.w*q.y - q.z*q.x); + y0 = y0 > 1.0f ? 1.0f : y0; + y0 = y0 < -1.0f ? -1.0f : y0; + result.y = asinf(y0); + + // Yaw (z-axis rotation) + float z0 = 2.0f*(q.w*q.z + q.x*q.y); + float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); + result.z = atan2f(z0, z1); + + return result; +} + +// Transform a quaternion given a transformation matrix +RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat) +{ + Quaternion result = { 0 }; + + result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; + result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; + result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; + result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; + + return result; +} + +// Check whether two given quaternions are almost equal +RMAPI int QuaternionEquals(Quaternion p, Quaternion q) +{ + int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && + ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) || + (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && + ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))); + + return result; +} + +#endif // RAYMATH_H diff --git a/build/vendor/raylib/rlgl.h b/modules/vendor/raylib/rlgl.h similarity index 97% rename from build/vendor/raylib/rlgl.h rename to modules/vendor/raylib/rlgl.h index 86208de..abfb52b 100644 --- a/build/vendor/raylib/rlgl.h +++ b/modules/vendor/raylib/rlgl.h @@ -1,4740 +1,4740 @@ -/********************************************************************************************** -* -* rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API -* -* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) -* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) -* -* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are -* initialized on rlglInit() to accumulate vertex data. -* -* When an internal state change is required all the stored vertex data is renderer in batch, -* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. -* -* Some additional resources are also loaded for convenience, here the complete list: -* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data -* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8 -* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs) -* -* Internal buffer (and additional resources) must be manually unloaded calling rlglClose(). -* -* -* CONFIGURATION: -* -* #define GRAPHICS_API_OPENGL_11 -* #define GRAPHICS_API_OPENGL_21 -* #define GRAPHICS_API_OPENGL_33 -* #define GRAPHICS_API_OPENGL_43 -* #define GRAPHICS_API_OPENGL_ES2 -* Use selected OpenGL graphics backend, should be supported by platform -* Those preprocessor defines are only used on rlgl module, if OpenGL version is -* required by any other module, use rlGetVersion() to check it -* -* #define RLGL_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* #define RLGL_RENDER_TEXTURES_HINT -* Enable framebuffer objects (fbo) support (enabled by default) -* Some GPUs could not support them despite the OpenGL version -* -* #define RLGL_SHOW_GL_DETAILS_INFO -* Show OpenGL extensions and capabilities detailed logs on init -* -* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT -* Enable debug context (only available on OpenGL 4.3) -* -* rlgl capabilities could be customized just defining some internal -* values before library inclusion (default values listed): -* -* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits -* #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) -* #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) -* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) -* -* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack -* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported -* #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance -* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance -* -* When loading a shader, the following vertex attribute and uniform -* location names are tried to be set automatically: -* -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) -* -* DEPENDENCIES: -* -* - OpenGL libraries (depending on platform and OpenGL version selected) -* - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core) -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RLGL_H -#define RLGL_H - -#define RLGL_VERSION "4.5" - -// Function specifiers in case library is build/used as a shared library (Windows) -// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll -#if defined(_WIN32) - #if defined(BUILD_LIBTYPE_SHARED) - #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) - #elif defined(USE_LIBTYPE_SHARED) - #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) - #endif -#endif - -// Function specifiers definition -#ifndef RLAPI - #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) -#endif - -// Support TRACELOG macros -#ifndef TRACELOG - #define TRACELOG(level, ...) (void)0 - #define TRACELOGD(...) (void)0 -#endif - -// Allow custom memory allocators -#ifndef RL_MALLOC - #define RL_MALLOC(sz) malloc(sz) -#endif -#ifndef RL_CALLOC - #define RL_CALLOC(n,sz) calloc(n,sz) -#endif -#ifndef RL_REALLOC - #define RL_REALLOC(n,sz) realloc(n,sz) -#endif -#ifndef RL_FREE - #define RL_FREE(p) free(p) -#endif - -// Security check in case no GRAPHICS_API_OPENGL_* defined -#if !defined(GRAPHICS_API_OPENGL_11) && \ - !defined(GRAPHICS_API_OPENGL_21) && \ - !defined(GRAPHICS_API_OPENGL_33) && \ - !defined(GRAPHICS_API_OPENGL_43) && \ - !defined(GRAPHICS_API_OPENGL_ES2) - #define GRAPHICS_API_OPENGL_33 -#endif - -// Security check in case multiple GRAPHICS_API_OPENGL_* defined -#if defined(GRAPHICS_API_OPENGL_11) - #if defined(GRAPHICS_API_OPENGL_21) - #undef GRAPHICS_API_OPENGL_21 - #endif - #if defined(GRAPHICS_API_OPENGL_33) - #undef GRAPHICS_API_OPENGL_33 - #endif - #if defined(GRAPHICS_API_OPENGL_43) - #undef GRAPHICS_API_OPENGL_43 - #endif - #if defined(GRAPHICS_API_OPENGL_ES2) - #undef GRAPHICS_API_OPENGL_ES2 - #endif -#endif - -// OpenGL 2.1 uses most of OpenGL 3.3 Core functionality -// WARNING: Specific parts are checked with #if defines -#if defined(GRAPHICS_API_OPENGL_21) - #define GRAPHICS_API_OPENGL_33 -#endif - -// OpenGL 4.3 uses OpenGL 3.3 Core functionality -#if defined(GRAPHICS_API_OPENGL_43) - #define GRAPHICS_API_OPENGL_33 -#endif - -// Support framebuffer objects by default -// NOTE: Some driver implementation do not support it, despite they should -#define RLGL_RENDER_TEXTURES_HINT - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- - -// Default internal render batch elements limits -#ifndef RL_DEFAULT_BATCH_BUFFER_ELEMENTS - #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - // This is the maximum amount of elements (quads) per batch - // NOTE: Be careful with text, every letter maps to a quad - #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 - #endif - #if defined(GRAPHICS_API_OPENGL_ES2) - // We reduce memory sizes for embedded systems (RPI and HTML5) - // NOTE: On HTML5 (emscripten) this is allocated on heap, - // by default it's only 16MB!...just take care... - #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 2048 - #endif -#endif -#ifndef RL_DEFAULT_BATCH_BUFFERS - #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) -#endif -#ifndef RL_DEFAULT_BATCH_DRAWCALLS - #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) -#endif -#ifndef RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS - #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) -#endif - -// Internal Matrix stack -#ifndef RL_MAX_MATRIX_STACK_SIZE - #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of Matrix stack -#endif - -// Shader limits -#ifndef RL_MAX_SHADER_LOCATIONS - #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported -#endif - -// Projection matrix culling -#ifndef RL_CULL_DISTANCE_NEAR - #define RL_CULL_DISTANCE_NEAR 0.01 // Default near cull distance -#endif -#ifndef RL_CULL_DISTANCE_FAR - #define RL_CULL_DISTANCE_FAR 1000.0 // Default far cull distance -#endif - -// Texture parameters (equivalent to OpenGL defines) -#define RL_TEXTURE_WRAP_S 0x2802 // GL_TEXTURE_WRAP_S -#define RL_TEXTURE_WRAP_T 0x2803 // GL_TEXTURE_WRAP_T -#define RL_TEXTURE_MAG_FILTER 0x2800 // GL_TEXTURE_MAG_FILTER -#define RL_TEXTURE_MIN_FILTER 0x2801 // GL_TEXTURE_MIN_FILTER - -#define RL_TEXTURE_FILTER_NEAREST 0x2600 // GL_NEAREST -#define RL_TEXTURE_FILTER_LINEAR 0x2601 // GL_LINEAR -#define RL_TEXTURE_FILTER_MIP_NEAREST 0x2700 // GL_NEAREST_MIPMAP_NEAREST -#define RL_TEXTURE_FILTER_NEAREST_MIP_LINEAR 0x2702 // GL_NEAREST_MIPMAP_LINEAR -#define RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST 0x2701 // GL_LINEAR_MIPMAP_NEAREST -#define RL_TEXTURE_FILTER_MIP_LINEAR 0x2703 // GL_LINEAR_MIPMAP_LINEAR -#define RL_TEXTURE_FILTER_ANISOTROPIC 0x3000 // Anisotropic filter (custom identifier) -#define RL_TEXTURE_MIPMAP_BIAS_RATIO 0x4000 // Texture mipmap bias, percentage ratio (custom identifier) - -#define RL_TEXTURE_WRAP_REPEAT 0x2901 // GL_REPEAT -#define RL_TEXTURE_WRAP_CLAMP 0x812F // GL_CLAMP_TO_EDGE -#define RL_TEXTURE_WRAP_MIRROR_REPEAT 0x8370 // GL_MIRRORED_REPEAT -#define RL_TEXTURE_WRAP_MIRROR_CLAMP 0x8742 // GL_MIRROR_CLAMP_EXT - -// Matrix modes (equivalent to OpenGL) -#define RL_MODELVIEW 0x1700 // GL_MODELVIEW -#define RL_PROJECTION 0x1701 // GL_PROJECTION -#define RL_TEXTURE 0x1702 // GL_TEXTURE - -// Primitive assembly draw modes -#define RL_LINES 0x0001 // GL_LINES -#define RL_TRIANGLES 0x0004 // GL_TRIANGLES -#define RL_QUADS 0x0007 // GL_QUADS - -// GL equivalent data types -#define RL_UNSIGNED_BYTE 0x1401 // GL_UNSIGNED_BYTE -#define RL_FLOAT 0x1406 // GL_FLOAT - -// GL buffer usage hint -#define RL_STREAM_DRAW 0x88E0 // GL_STREAM_DRAW -#define RL_STREAM_READ 0x88E1 // GL_STREAM_READ -#define RL_STREAM_COPY 0x88E2 // GL_STREAM_COPY -#define RL_STATIC_DRAW 0x88E4 // GL_STATIC_DRAW -#define RL_STATIC_READ 0x88E5 // GL_STATIC_READ -#define RL_STATIC_COPY 0x88E6 // GL_STATIC_COPY -#define RL_DYNAMIC_DRAW 0x88E8 // GL_DYNAMIC_DRAW -#define RL_DYNAMIC_READ 0x88E9 // GL_DYNAMIC_READ -#define RL_DYNAMIC_COPY 0x88EA // GL_DYNAMIC_COPY - -// GL Shader type -#define RL_FRAGMENT_SHADER 0x8B30 // GL_FRAGMENT_SHADER -#define RL_VERTEX_SHADER 0x8B31 // GL_VERTEX_SHADER -#define RL_COMPUTE_SHADER 0x91B9 // GL_COMPUTE_SHADER - -// GL blending factors -#define RL_ZERO 0 // GL_ZERO -#define RL_ONE 1 // GL_ONE -#define RL_SRC_COLOR 0x0300 // GL_SRC_COLOR -#define RL_ONE_MINUS_SRC_COLOR 0x0301 // GL_ONE_MINUS_SRC_COLOR -#define RL_SRC_ALPHA 0x0302 // GL_SRC_ALPHA -#define RL_ONE_MINUS_SRC_ALPHA 0x0303 // GL_ONE_MINUS_SRC_ALPHA -#define RL_DST_ALPHA 0x0304 // GL_DST_ALPHA -#define RL_ONE_MINUS_DST_ALPHA 0x0305 // GL_ONE_MINUS_DST_ALPHA -#define RL_DST_COLOR 0x0306 // GL_DST_COLOR -#define RL_ONE_MINUS_DST_COLOR 0x0307 // GL_ONE_MINUS_DST_COLOR -#define RL_SRC_ALPHA_SATURATE 0x0308 // GL_SRC_ALPHA_SATURATE -#define RL_CONSTANT_COLOR 0x8001 // GL_CONSTANT_COLOR -#define RL_ONE_MINUS_CONSTANT_COLOR 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR -#define RL_CONSTANT_ALPHA 0x8003 // GL_CONSTANT_ALPHA -#define RL_ONE_MINUS_CONSTANT_ALPHA 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA - -// GL blending functions/equations -#define RL_FUNC_ADD 0x8006 // GL_FUNC_ADD -#define RL_MIN 0x8007 // GL_MIN -#define RL_MAX 0x8008 // GL_MAX -#define RL_FUNC_SUBTRACT 0x800A // GL_FUNC_SUBTRACT -#define RL_FUNC_REVERSE_SUBTRACT 0x800B // GL_FUNC_REVERSE_SUBTRACT -#define RL_BLEND_EQUATION 0x8009 // GL_BLEND_EQUATION -#define RL_BLEND_EQUATION_RGB 0x8009 // GL_BLEND_EQUATION_RGB // (Same as BLEND_EQUATION) -#define RL_BLEND_EQUATION_ALPHA 0x883D // GL_BLEND_EQUATION_ALPHA -#define RL_BLEND_DST_RGB 0x80C8 // GL_BLEND_DST_RGB -#define RL_BLEND_SRC_RGB 0x80C9 // GL_BLEND_SRC_RGB -#define RL_BLEND_DST_ALPHA 0x80CA // GL_BLEND_DST_ALPHA -#define RL_BLEND_SRC_ALPHA 0x80CB // GL_BLEND_SRC_ALPHA -#define RL_BLEND_COLOR 0x8005 // GL_BLEND_COLOR - - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) - #include -#elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE) - // Boolean type -typedef enum bool { false = 0, true = !false } bool; -#endif - -#if !defined(RL_MATRIX_TYPE) -// Matrix, 4x4 components, column major, OpenGL style, right handed -typedef struct Matrix { - float m0, m4, m8, m12; // Matrix first row (4 components) - float m1, m5, m9, m13; // Matrix second row (4 components) - float m2, m6, m10, m14; // Matrix third row (4 components) - float m3, m7, m11, m15; // Matrix fourth row (4 components) -} Matrix; -#define RL_MATRIX_TYPE -#endif - -// Dynamic vertex buffers (position + texcoords + colors + indices arrays) -typedef struct rlVertexBuffer { - int elementCount; // Number of elements in the buffer (QUADS) - - float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) - float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) - unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) -#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - unsigned int *indices; // Vertex indices (in case vertex data comes indexed) (6 indices per quad) -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) - unsigned short *indices; // Vertex indices (in case vertex data comes indexed) (6 indices per quad) -#endif - unsigned int vaoId; // OpenGL Vertex Array Object id - unsigned int vboId[4]; // OpenGL Vertex Buffer Objects id (4 types of vertex data) -} rlVertexBuffer; - -// Draw call type -// NOTE: Only texture changes register a new draw, other state-change-related elements are not -// used at this moment (vaoId, shaderId, matrices), raylib just forces a batch draw call if any -// of those state-change happens (this is done in core module) -typedef struct rlDrawCall { - int mode; // Drawing mode: LINES, TRIANGLES, QUADS - int vertexCount; // Number of vertex of the draw - int vertexAlignment; // Number of vertex required for index alignment (LINES, TRIANGLES) - //unsigned int vaoId; // Vertex array id to be used on the draw -> Using RLGL.currentBatch->vertexBuffer.vaoId - //unsigned int shaderId; // Shader id to be used on the draw -> Using RLGL.currentShaderId - unsigned int textureId; // Texture id to be used on the draw -> Use to create new draw call if changes - - //Matrix projection; // Projection matrix for this draw -> Using RLGL.projection by default - //Matrix modelview; // Modelview matrix for this draw -> Using RLGL.modelview by default -} rlDrawCall; - -// rlRenderBatch type -typedef struct rlRenderBatch { - int bufferCount; // Number of vertex buffers (multi-buffering support) - int currentBuffer; // Current buffer tracking in case of multi-buffering - rlVertexBuffer *vertexBuffer; // Dynamic buffer(s) for vertex data - - rlDrawCall *draws; // Draw calls array, depends on textureId - int drawCounter; // Draw calls counter - float currentDepth; // Current depth value for next draw -} rlRenderBatch; - -// OpenGL version -typedef enum { - RL_OPENGL_11 = 1, // OpenGL 1.1 - RL_OPENGL_21, // OpenGL 2.1 (GLSL 120) - RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) - RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330) - RL_OPENGL_ES_20 // OpenGL ES 2.0 (GLSL 100) -} rlGlVersion; - -// Trace log level -// NOTE: Organized by priority level -typedef enum { - RL_LOG_ALL = 0, // Display all logs - RL_LOG_TRACE, // Trace logging, intended for internal use only - RL_LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds - RL_LOG_INFO, // Info logging, used for program execution info - RL_LOG_WARNING, // Warning logging, used on recoverable failures - RL_LOG_ERROR, // Error logging, used on unrecoverable failures - RL_LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) - RL_LOG_NONE // Disable logging -} rlTraceLogLevel; - -// Texture pixel formats -// NOTE: Support depends on OpenGL version -typedef enum { - RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) - RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) - RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp - RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp - RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) - RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) - RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp - RL_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) - RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) - RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) - RL_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) - RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) - RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp - RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp - RL_PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp - RL_PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp - RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp - RL_PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp - RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp - RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp - RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp -} rlPixelFormat; - -// Texture parameters: filter mode -// NOTE 1: Filtering considers mipmaps if available in the texture -// NOTE 2: Filter is accordingly set for minification and magnification -typedef enum { - RL_TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation - RL_TEXTURE_FILTER_BILINEAR, // Linear filtering - RL_TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) - RL_TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x - RL_TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x - RL_TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x -} rlTextureFilter; - -// Color blending modes (pre-defined) -typedef enum { - RL_BLEND_ALPHA = 0, // Blend textures considering alpha (default) - RL_BLEND_ADDITIVE, // Blend textures adding colors - RL_BLEND_MULTIPLIED, // Blend textures multiplying colors - RL_BLEND_ADD_COLORS, // Blend textures adding colors (alternative) - RL_BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) - RL_BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha - RL_BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) - RL_BLEND_CUSTOM_SEPARATE // Blend textures using custom src/dst factors (use rlSetBlendFactorsSeparate()) -} rlBlendMode; - -// Shader location point type -typedef enum { - RL_SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position - RL_SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 - RL_SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 - RL_SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal - RL_SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent - RL_SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color - RL_SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection - RL_SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) - RL_SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection - RL_SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) - RL_SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal - RL_SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view - RL_SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color - RL_SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color - RL_SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color - RL_SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: RL_SHADER_LOC_MAP_DIFFUSE) - RL_SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: RL_SHADER_LOC_MAP_SPECULAR) - RL_SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal - RL_SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness - RL_SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion - RL_SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission - RL_SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height - RL_SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap - RL_SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance - RL_SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter - RL_SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf -} rlShaderLocationIndex; - -#define RL_SHADER_LOC_MAP_DIFFUSE RL_SHADER_LOC_MAP_ALBEDO -#define RL_SHADER_LOC_MAP_SPECULAR RL_SHADER_LOC_MAP_METALNESS - -// Shader uniform data type -typedef enum { - RL_SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float - RL_SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) - RL_SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) - RL_SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float) - RL_SHADER_UNIFORM_INT, // Shader uniform type: int - RL_SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) - RL_SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) - RL_SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) - RL_SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d -} rlShaderUniformDataType; - -// Shader attribute data types -typedef enum { - RL_SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float - RL_SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float) - RL_SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float) - RL_SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) -} rlShaderAttributeDataType; - -// Framebuffer attachment type -// NOTE: By default up to 8 color channels defined, but it can be more -typedef enum { - RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 - RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1 - RL_ATTACHMENT_COLOR_CHANNEL2, // Framebuffer attachment type: color 2 - RL_ATTACHMENT_COLOR_CHANNEL3, // Framebuffer attachment type: color 3 - RL_ATTACHMENT_COLOR_CHANNEL4, // Framebuffer attachment type: color 4 - RL_ATTACHMENT_COLOR_CHANNEL5, // Framebuffer attachment type: color 5 - RL_ATTACHMENT_COLOR_CHANNEL6, // Framebuffer attachment type: color 6 - RL_ATTACHMENT_COLOR_CHANNEL7, // Framebuffer attachment type: color 7 - RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth - RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil -} rlFramebufferAttachType; - -// Framebuffer texture attachment type -typedef enum { - RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_X, // Framebuffer texture attachment type: cubemap, -X side - RL_ATTACHMENT_CUBEMAP_POSITIVE_Y, // Framebuffer texture attachment type: cubemap, +Y side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y, // Framebuffer texture attachment type: cubemap, -Y side - RL_ATTACHMENT_CUBEMAP_POSITIVE_Z, // Framebuffer texture attachment type: cubemap, +Z side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z, // Framebuffer texture attachment type: cubemap, -Z side - RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d - RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer -} rlFramebufferAttachTextureType; - -// Face culling mode -typedef enum { - RL_CULL_FACE_FRONT = 0, - RL_CULL_FACE_BACK -} rlCullMode; - -//------------------------------------------------------------------------------------ -// Functions Declaration - Matrix operations -//------------------------------------------------------------------------------------ - -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - -RLAPI void rlMatrixMode(int mode); // Choose the current matrix to be transformed -RLAPI void rlPushMatrix(void); // Push the current matrix to stack -RLAPI void rlPopMatrix(void); // Pop latest inserted matrix from stack -RLAPI void rlLoadIdentity(void); // Reset current matrix to identity matrix -RLAPI void rlTranslatef(float x, float y, float z); // Multiply the current matrix by a translation matrix -RLAPI void rlRotatef(float angle, float x, float y, float z); // Multiply the current matrix by a rotation matrix -RLAPI void rlScalef(float x, float y, float z); // Multiply the current matrix by a scaling matrix -RLAPI void rlMultMatrixf(const float *matf); // Multiply the current matrix by another matrix -RLAPI void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar); -RLAPI void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar); -RLAPI void rlViewport(int x, int y, int width, int height); // Set the viewport area - -//------------------------------------------------------------------------------------ -// Functions Declaration - Vertex level operations -//------------------------------------------------------------------------------------ -RLAPI void rlBegin(int mode); // Initialize drawing mode (how to organize vertex) -RLAPI void rlEnd(void); // Finish vertex providing -RLAPI void rlVertex2i(int x, int y); // Define one vertex (position) - 2 int -RLAPI void rlVertex2f(float x, float y); // Define one vertex (position) - 2 float -RLAPI void rlVertex3f(float x, float y, float z); // Define one vertex (position) - 3 float -RLAPI void rlTexCoord2f(float x, float y); // Define one vertex (texture coordinate) - 2 float -RLAPI void rlNormal3f(float x, float y, float z); // Define one vertex (normal) - 3 float -RLAPI void rlColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a); // Define one vertex (color) - 4 byte -RLAPI void rlColor3f(float x, float y, float z); // Define one vertex (color) - 3 float -RLAPI void rlColor4f(float x, float y, float z, float w); // Define one vertex (color) - 4 float - -//------------------------------------------------------------------------------------ -// Functions Declaration - OpenGL style functions (common to 1.1, 3.3+, ES2) -// NOTE: This functions are used to completely abstract raylib code from OpenGL layer, -// some of them are direct wrappers over OpenGL calls, some others are custom -//------------------------------------------------------------------------------------ - -// Vertex buffers state -RLAPI bool rlEnableVertexArray(unsigned int vaoId); // Enable vertex array (VAO, if supported) -RLAPI void rlDisableVertexArray(void); // Disable vertex array (VAO, if supported) -RLAPI void rlEnableVertexBuffer(unsigned int id); // Enable vertex buffer (VBO) -RLAPI void rlDisableVertexBuffer(void); // Disable vertex buffer (VBO) -RLAPI void rlEnableVertexBufferElement(unsigned int id);// Enable vertex buffer element (VBO element) -RLAPI void rlDisableVertexBufferElement(void); // Disable vertex buffer element (VBO element) -RLAPI void rlEnableVertexAttribute(unsigned int index); // Enable vertex attribute index -RLAPI void rlDisableVertexAttribute(unsigned int index);// Disable vertex attribute index -#if defined(GRAPHICS_API_OPENGL_11) -RLAPI void rlEnableStatePointer(int vertexAttribType, void *buffer); // Enable attribute state pointer -RLAPI void rlDisableStatePointer(int vertexAttribType); // Disable attribute state pointer -#endif - -// Textures state -RLAPI void rlActiveTextureSlot(int slot); // Select and active a texture slot -RLAPI void rlEnableTexture(unsigned int id); // Enable texture -RLAPI void rlDisableTexture(void); // Disable texture -RLAPI void rlEnableTextureCubemap(unsigned int id); // Enable texture cubemap -RLAPI void rlDisableTextureCubemap(void); // Disable texture cubemap -RLAPI void rlTextureParameters(unsigned int id, int param, int value); // Set texture parameters (filter, wrap) -RLAPI void rlCubemapParameters(unsigned int id, int param, int value); // Set cubemap parameters (filter, wrap) - -// Shader state -RLAPI void rlEnableShader(unsigned int id); // Enable shader program -RLAPI void rlDisableShader(void); // Disable shader program - -// Framebuffer state -RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo) -RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer -RLAPI void rlActiveDrawBuffers(int count); // Activate multiple draw color buffers - -// General render state -RLAPI void rlEnableColorBlend(void); // Enable color blending -RLAPI void rlDisableColorBlend(void); // Disable color blending -RLAPI void rlEnableDepthTest(void); // Enable depth test -RLAPI void rlDisableDepthTest(void); // Disable depth test -RLAPI void rlEnableDepthMask(void); // Enable depth write -RLAPI void rlDisableDepthMask(void); // Disable depth write -RLAPI void rlEnableBackfaceCulling(void); // Enable backface culling -RLAPI void rlDisableBackfaceCulling(void); // Disable backface culling -RLAPI void rlSetCullFace(int mode); // Set face culling mode -RLAPI void rlEnableScissorTest(void); // Enable scissor test -RLAPI void rlDisableScissorTest(void); // Disable scissor test -RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test -RLAPI void rlEnableWireMode(void); // Enable wire mode -RLAPI void rlDisableWireMode(void); // Disable wire mode -RLAPI void rlSetLineWidth(float width); // Set the line drawing width -RLAPI float rlGetLineWidth(void); // Get the line drawing width -RLAPI void rlEnableSmoothLines(void); // Enable line aliasing -RLAPI void rlDisableSmoothLines(void); // Disable line aliasing -RLAPI void rlEnableStereoRender(void); // Enable stereo rendering -RLAPI void rlDisableStereoRender(void); // Disable stereo rendering -RLAPI bool rlIsStereoRenderEnabled(void); // Check if stereo render is enabled - -RLAPI void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a); // Clear color buffer with color -RLAPI void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) -RLAPI void rlCheckErrors(void); // Check and log OpenGL error codes -RLAPI void rlSetBlendMode(int mode); // Set blending mode -RLAPI void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation); // Set blending mode factor and equation (using OpenGL factors) -RLAPI void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha); // Set blending mode factors and equations separately (using OpenGL factors) - -//------------------------------------------------------------------------------------ -// Functions Declaration - rlgl functionality -//------------------------------------------------------------------------------------ -// rlgl initialization functions -RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) -RLAPI void rlglClose(void); // De-initialize rlgl (buffers, shaders, textures) -RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required) -RLAPI int rlGetVersion(void); // Get current OpenGL version -RLAPI void rlSetFramebufferWidth(int width); // Set current framebuffer width -RLAPI int rlGetFramebufferWidth(void); // Get default framebuffer width -RLAPI void rlSetFramebufferHeight(int height); // Set current framebuffer height -RLAPI int rlGetFramebufferHeight(void); // Get default framebuffer height - -RLAPI unsigned int rlGetTextureIdDefault(void); // Get default texture id -RLAPI unsigned int rlGetShaderIdDefault(void); // Get default shader id -RLAPI int *rlGetShaderLocsDefault(void); // Get default shader locations - -// Render batch management -// NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode -// but this render batch API is exposed in case of custom batches are required -RLAPI rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements); // Load a render batch system -RLAPI void rlUnloadRenderBatch(rlRenderBatch batch); // Unload render batch system -RLAPI void rlDrawRenderBatch(rlRenderBatch *batch); // Draw render batch data (Update->Draw->Reset) -RLAPI void rlSetRenderBatchActive(rlRenderBatch *batch); // Set the active render batch for rlgl (NULL for default internal) -RLAPI void rlDrawRenderBatchActive(void); // Update and draw internal render batch -RLAPI bool rlCheckRenderBatchLimit(int vCount); // Check internal buffer overflow for a given number of vertex - -RLAPI void rlSetTexture(unsigned int id); // Set current texture for render batch and check buffers limits - -//------------------------------------------------------------------------------------------------------------------------ - -// Vertex buffers management -RLAPI unsigned int rlLoadVertexArray(void); // Load vertex array (vao) if supported -RLAPI unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic); // Load a vertex buffer attribute -RLAPI unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic); // Load a new attributes element buffer -RLAPI void rlUpdateVertexBuffer(unsigned int bufferId, const void *data, int dataSize, int offset); // Update GPU buffer with new data -RLAPI void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset); // Update vertex buffer elements with new data -RLAPI void rlUnloadVertexArray(unsigned int vaoId); -RLAPI void rlUnloadVertexBuffer(unsigned int vboId); -RLAPI void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, const void *pointer); -RLAPI void rlSetVertexAttributeDivisor(unsigned int index, int divisor); -RLAPI void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count); // Set vertex attribute default value -RLAPI void rlDrawVertexArray(int offset, int count); -RLAPI void rlDrawVertexArrayElements(int offset, int count, const void *buffer); -RLAPI void rlDrawVertexArrayInstanced(int offset, int count, int instances); -RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances); - -// Textures management -RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU -RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo) -RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format); // Load texture cubemap -RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update GPU texture with new data -RLAPI void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats -RLAPI const char *rlGetPixelFormatName(unsigned int format); // Get name string for pixel format -RLAPI void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory -RLAPI void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps); // Generate mipmap data for selected texture -RLAPI void *rlReadTexturePixels(unsigned int id, int width, int height, int format); // Read texture pixel data -RLAPI unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) - -// Framebuffer management (fbo) -RLAPI unsigned int rlLoadFramebuffer(int width, int height); // Load an empty framebuffer -RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer -RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete -RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU - -// Shaders management -RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings -RLAPI unsigned int rlCompileShader(const char *shaderCode, int type); // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER) -RLAPI unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program -RLAPI void rlUnloadShaderProgram(unsigned int id); // Unload shader program -RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName); // Get shader location uniform -RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName); // Get shader location attribute -RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count); // Set shader value uniform -RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat); // Set shader value matrix -RLAPI void rlSetUniformSampler(int locIndex, unsigned int textureId); // Set shader value sampler -RLAPI void rlSetShader(unsigned int id, int *locs); // Set shader currently active (id and locations) - -// Compute shader management -RLAPI unsigned int rlLoadComputeShaderProgram(unsigned int shaderId); // Load compute shader program -RLAPI void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ); // Dispatch compute shader (equivalent to *draw* for graphics pipeline) - -// Shader buffer storage object management (ssbo) -RLAPI unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHint); // Load shader storage buffer object (SSBO) -RLAPI void rlUnloadShaderBuffer(unsigned int ssboId); // Unload shader storage buffer object (SSBO) -RLAPI void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSize, unsigned int offset); // Update SSBO buffer data -RLAPI void rlBindShaderBuffer(unsigned int id, unsigned int index); // Bind SSBO buffer -RLAPI void rlReadShaderBuffer(unsigned int id, void *dest, unsigned int count, unsigned int offset); // Read SSBO buffer data (GPU->CPU) -RLAPI void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned int destOffset, unsigned int srcOffset, unsigned int count); // Copy SSBO data between buffers -RLAPI unsigned int rlGetShaderBufferSize(unsigned int id); // Get SSBO buffer size - -// Buffer management -RLAPI void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool readonly); // Bind image texture - -// Matrix state management -RLAPI Matrix rlGetMatrixModelview(void); // Get internal modelview matrix -RLAPI Matrix rlGetMatrixProjection(void); // Get internal projection matrix -RLAPI Matrix rlGetMatrixTransform(void); // Get internal accumulated transform matrix -RLAPI Matrix rlGetMatrixProjectionStereo(int eye); // Get internal projection matrix for stereo render (selected eye) -RLAPI Matrix rlGetMatrixViewOffsetStereo(int eye); // Get internal view offset matrix for stereo render (selected eye) -RLAPI void rlSetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) -RLAPI void rlSetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) -RLAPI void rlSetMatrixProjectionStereo(Matrix right, Matrix left); // Set eyes projection matrices for stereo rendering -RLAPI void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left); // Set eyes view offsets matrices for stereo rendering - -// Quick and dirty cube/quad buffers load->draw->unload -RLAPI void rlLoadDrawCube(void); // Load and draw a cube -RLAPI void rlLoadDrawQuad(void); // Load and draw a quad - -#if defined(__cplusplus) -} -#endif - -#endif // RLGL_H - -/*********************************************************************************** -* -* RLGL IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RLGL_IMPLEMENTATION) - -#if defined(GRAPHICS_API_OPENGL_11) - #if defined(__APPLE__) - #include // OpenGL 1.1 library for OSX - #include // OpenGL extensions library - #else - // APIENTRY for OpenGL function pointer declarations is required - #if !defined(APIENTRY) - #if defined(_WIN32) - #define APIENTRY __stdcall - #else - #define APIENTRY - #endif - #endif - // WINGDIAPI definition. Some Windows OpenGL headers need it - #if !defined(WINGDIAPI) && defined(_WIN32) - #define WINGDIAPI __declspec(dllimport) - #endif - - #include // OpenGL 1.1 library - #endif -#endif - -#if defined(GRAPHICS_API_OPENGL_33) - #define GLAD_MALLOC RL_MALLOC - #define GLAD_FREE RL_FREE - - #define GLAD_GL_IMPLEMENTATION - #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers -#endif - -#if defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, - // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 - #if defined(PLATFORM_DESKTOP) - #define GLAD_GLES2_IMPLEMENTATION - #include "external/glad_gles2.h" - #else - #define GL_GLEXT_PROTOTYPES - //#include // EGL library -> not required, platform layer - #include // OpenGL ES 2.0 library - #include // OpenGL ES 2.0 extensions library - #endif - - // It seems OpenGL ES 2.0 instancing entry points are not defined on Raspberry Pi - // provided headers (despite being defined in official Khronos GLES2 headers) - #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) - typedef void (GL_APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); - typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); - typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); - #endif -#endif - -#include // Required for: malloc(), free() -#include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] -#include // Required for: sqrtf(), sinf(), cosf(), floor(), log() - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846f -#endif -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif - -#ifndef GL_SHADING_LANGUAGE_VERSION - #define GL_SHADING_LANGUAGE_VERSION 0x8B8C -#endif - -#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT - #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 -#endif -#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 -#endif -#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 -#endif -#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT - #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 -#endif -#ifndef GL_ETC1_RGB8_OES - #define GL_ETC1_RGB8_OES 0x8D64 -#endif -#ifndef GL_COMPRESSED_RGB8_ETC2 - #define GL_COMPRESSED_RGB8_ETC2 0x9274 -#endif -#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC - #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 -#endif -#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG - #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 -#endif -#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG - #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 -#endif -#ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR - #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 -#endif -#ifndef GL_COMPRESSED_RGBA_ASTC_8x8_KHR - #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 -#endif - -#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT - #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF -#endif -#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT - #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE -#endif - -#if defined(GRAPHICS_API_OPENGL_11) - #define GL_UNSIGNED_SHORT_5_6_5 0x8363 - #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 - #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 -#endif - -#if defined(GRAPHICS_API_OPENGL_21) - #define GL_LUMINANCE 0x1909 - #define GL_LUMINANCE_ALPHA 0x190A -#endif - -#if defined(GRAPHICS_API_OPENGL_ES2) - #define glClearDepth glClearDepthf - #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER - #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER -#endif - -// Default shader vertex attribute names to set location points -#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION - #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 -#endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD - #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 -#endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL - #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 -#endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR - #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 -#endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT - #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 -#endif -#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 - #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5 -#endif - -#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP - #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix -#endif -#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW - #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix -#endif -#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION - #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix -#endif -#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL - #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix -#endif -#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL - #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) -#endif -#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR - #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) -#endif -#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 - #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) -#endif -#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 - #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) -#endif -#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 - #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -typedef struct rlglData { - rlRenderBatch *currentBatch; // Current render batch - rlRenderBatch defaultBatch; // Default internal render batch - - struct { - int vertexCounter; // Current active render batch vertex counter (generic, used for all batches) - float texcoordx, texcoordy; // Current active texture coordinate (added on glVertex*()) - float normalx, normaly, normalz; // Current active normal (added on glVertex*()) - unsigned char colorr, colorg, colorb, colora; // Current active color (added on glVertex*()) - - int currentMatrixMode; // Current matrix mode - Matrix *currentMatrix; // Current matrix pointer - Matrix modelview; // Default modelview matrix - Matrix projection; // Default projection matrix - Matrix transform; // Transform matrix to be used with rlTranslate, rlRotate, rlScale - bool transformRequired; // Require transform matrix application to current draw-call vertex (if required) - Matrix stack[RL_MAX_MATRIX_STACK_SIZE];// Matrix stack for push/pop - int stackCounter; // Matrix stack counter - - unsigned int defaultTextureId; // Default texture used on shapes/poly drawing (required by shader) - unsigned int activeTextureId[RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS]; // Active texture ids to be enabled on batch drawing (0 active by default) - unsigned int defaultVShaderId; // Default vertex shader id (used by default shader program) - unsigned int defaultFShaderId; // Default fragment shader id (used by default shader program) - unsigned int defaultShaderId; // Default shader program id, supports vertex color and diffuse texture - int *defaultShaderLocs; // Default shader locations pointer to be used on rendering - unsigned int currentShaderId; // Current shader id to be used on rendering (by default, defaultShaderId) - int *currentShaderLocs; // Current shader locations pointer to be used on rendering (by default, defaultShaderLocs) - - bool stereoRender; // Stereo rendering flag - Matrix projectionStereo[2]; // VR stereo rendering eyes projection matrices - Matrix viewOffsetStereo[2]; // VR stereo rendering eyes view offset matrices - - // Blending variables - int currentBlendMode; // Blending mode active - int glBlendSrcFactor; // Blending source factor - int glBlendDstFactor; // Blending destination factor - int glBlendEquation; // Blending equation - int glBlendSrcFactorRGB; // Blending source RGB factor - int glBlendDestFactorRGB; // Blending destination RGB factor - int glBlendSrcFactorAlpha; // Blending source alpha factor - int glBlendDestFactorAlpha; // Blending destination alpha factor - int glBlendEquationRGB; // Blending equation for RGB - int glBlendEquationAlpha; // Blending equation for alpha - bool glCustomBlendModeModified; // Custom blending factor and equation modification status - - int framebufferWidth; // Current framebuffer width - int framebufferHeight; // Current framebuffer height - - } State; // Renderer state - struct { - bool vao; // VAO support (OpenGL ES2 could not support VAO extension) (GL_ARB_vertex_array_object) - bool instancing; // Instancing supported (GL_ANGLE_instanced_arrays, GL_EXT_draw_instanced + GL_EXT_instanced_arrays) - bool texNPOT; // NPOT textures full support (GL_ARB_texture_non_power_of_two, GL_OES_texture_npot) - bool texDepth; // Depth textures supported (GL_ARB_depth_texture, GL_OES_depth_texture) - bool texDepthWebGL; // Depth textures supported WebGL specific (GL_WEBGL_depth_texture) - bool texFloat32; // float textures support (32 bit per channel) (GL_OES_texture_float) - bool texCompDXT; // DDS texture compression support (GL_EXT_texture_compression_s3tc, GL_WEBGL_compressed_texture_s3tc, GL_WEBKIT_WEBGL_compressed_texture_s3tc) - bool texCompETC1; // ETC1 texture compression support (GL_OES_compressed_ETC1_RGB8_texture, GL_WEBGL_compressed_texture_etc1) - bool texCompETC2; // ETC2/EAC texture compression support (GL_ARB_ES3_compatibility) - bool texCompPVRT; // PVR texture compression support (GL_IMG_texture_compression_pvrtc) - bool texCompASTC; // ASTC texture compression support (GL_KHR_texture_compression_astc_hdr, GL_KHR_texture_compression_astc_ldr) - bool texMirrorClamp; // Clamp mirror wrap mode supported (GL_EXT_texture_mirror_clamp) - bool texAnisoFilter; // Anisotropic texture filtering support (GL_EXT_texture_filter_anisotropic) - bool computeShader; // Compute shaders support (GL_ARB_compute_shader) - bool ssbo; // Shader storage buffer object support (GL_ARB_shader_storage_buffer_object) - - float maxAnisotropyLevel; // Maximum anisotropy level supported (minimum is 2.0f) - int maxDepthBits; // Maximum bits for depth component - - } ExtSupported; // Extensions supported flags -} rlglData; - -typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions loader signature (same as GLADloadproc) - -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -static rlglData RLGL = { 0 }; -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 - -#if defined(GRAPHICS_API_OPENGL_ES2) -// NOTE: VAO functionality is exposed through extensions (OES) -static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL; -static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL; -static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL; - -// NOTE: Instancing functionality could also be available through extension -static PFNGLDRAWARRAYSINSTANCEDEXTPROC glDrawArraysInstanced = NULL; -static PFNGLDRAWELEMENTSINSTANCEDEXTPROC glDrawElementsInstanced = NULL; -static PFNGLVERTEXATTRIBDIVISOREXTPROC glVertexAttribDivisor = NULL; -#endif - -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -static void rlLoadShaderDefault(void); // Load default shader -static void rlUnloadShaderDefault(void); // Unload default shader -#if defined(RLGL_SHOW_GL_DETAILS_INFO) -static char *rlGetCompressedFormatName(int format); // Get compressed format official GL identifier name -#endif // RLGL_SHOW_GL_DETAILS_INFO -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 - -static int rlGetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) - -// Auxiliar matrix math functions -static Matrix rlMatrixIdentity(void); // Get identity matrix -static Matrix rlMatrixMultiply(Matrix left, Matrix right); // Multiply two matrices - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Matrix operations -//---------------------------------------------------------------------------------- - -#if defined(GRAPHICS_API_OPENGL_11) -// Fallback to OpenGL 1.1 function calls -//--------------------------------------- -void rlMatrixMode(int mode) -{ - switch (mode) - { - case RL_PROJECTION: glMatrixMode(GL_PROJECTION); break; - case RL_MODELVIEW: glMatrixMode(GL_MODELVIEW); break; - case RL_TEXTURE: glMatrixMode(GL_TEXTURE); break; - default: break; - } -} - -void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar) -{ - glFrustum(left, right, bottom, top, znear, zfar); -} - -void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar) -{ - glOrtho(left, right, bottom, top, znear, zfar); -} - -void rlPushMatrix(void) { glPushMatrix(); } -void rlPopMatrix(void) { glPopMatrix(); } -void rlLoadIdentity(void) { glLoadIdentity(); } -void rlTranslatef(float x, float y, float z) { glTranslatef(x, y, z); } -void rlRotatef(float angle, float x, float y, float z) { glRotatef(angle, x, y, z); } -void rlScalef(float x, float y, float z) { glScalef(x, y, z); } -void rlMultMatrixf(const float *matf) { glMultMatrixf(matf); } -#endif -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -// Choose the current matrix to be transformed -void rlMatrixMode(int mode) -{ - if (mode == RL_PROJECTION) RLGL.State.currentMatrix = &RLGL.State.projection; - else if (mode == RL_MODELVIEW) RLGL.State.currentMatrix = &RLGL.State.modelview; - //else if (mode == RL_TEXTURE) // Not supported - - RLGL.State.currentMatrixMode = mode; -} - -// Push the current matrix into RLGL.State.stack -void rlPushMatrix(void) -{ - if (RLGL.State.stackCounter >= RL_MAX_MATRIX_STACK_SIZE) TRACELOG(RL_LOG_ERROR, "RLGL: Matrix stack overflow (RL_MAX_MATRIX_STACK_SIZE)"); - - if (RLGL.State.currentMatrixMode == RL_MODELVIEW) - { - RLGL.State.transformRequired = true; - RLGL.State.currentMatrix = &RLGL.State.transform; - } - - RLGL.State.stack[RLGL.State.stackCounter] = *RLGL.State.currentMatrix; - RLGL.State.stackCounter++; -} - -// Pop lattest inserted matrix from RLGL.State.stack -void rlPopMatrix(void) -{ - if (RLGL.State.stackCounter > 0) - { - Matrix mat = RLGL.State.stack[RLGL.State.stackCounter - 1]; - *RLGL.State.currentMatrix = mat; - RLGL.State.stackCounter--; - } - - if ((RLGL.State.stackCounter == 0) && (RLGL.State.currentMatrixMode == RL_MODELVIEW)) - { - RLGL.State.currentMatrix = &RLGL.State.modelview; - RLGL.State.transformRequired = false; - } -} - -// Reset current matrix to identity matrix -void rlLoadIdentity(void) -{ - *RLGL.State.currentMatrix = rlMatrixIdentity(); -} - -// Multiply the current matrix by a translation matrix -void rlTranslatef(float x, float y, float z) -{ - Matrix matTranslation = { - 1.0f, 0.0f, 0.0f, x, - 0.0f, 1.0f, 0.0f, y, - 0.0f, 0.0f, 1.0f, z, - 0.0f, 0.0f, 0.0f, 1.0f - }; - - // NOTE: We transpose matrix with multiplication order - *RLGL.State.currentMatrix = rlMatrixMultiply(matTranslation, *RLGL.State.currentMatrix); -} - -// Multiply the current matrix by a rotation matrix -// NOTE: The provided angle must be in degrees -void rlRotatef(float angle, float x, float y, float z) -{ - Matrix matRotation = rlMatrixIdentity(); - - // Axis vector (x, y, z) normalization - float lengthSquared = x*x + y*y + z*z; - if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) - { - float inverseLength = 1.0f/sqrtf(lengthSquared); - x *= inverseLength; - y *= inverseLength; - z *= inverseLength; - } - - // Rotation matrix generation - float sinres = sinf(DEG2RAD*angle); - float cosres = cosf(DEG2RAD*angle); - float t = 1.0f - cosres; - - matRotation.m0 = x*x*t + cosres; - matRotation.m1 = y*x*t + z*sinres; - matRotation.m2 = z*x*t - y*sinres; - matRotation.m3 = 0.0f; - - matRotation.m4 = x*y*t - z*sinres; - matRotation.m5 = y*y*t + cosres; - matRotation.m6 = z*y*t + x*sinres; - matRotation.m7 = 0.0f; - - matRotation.m8 = x*z*t + y*sinres; - matRotation.m9 = y*z*t - x*sinres; - matRotation.m10 = z*z*t + cosres; - matRotation.m11 = 0.0f; - - matRotation.m12 = 0.0f; - matRotation.m13 = 0.0f; - matRotation.m14 = 0.0f; - matRotation.m15 = 1.0f; - - // NOTE: We transpose matrix with multiplication order - *RLGL.State.currentMatrix = rlMatrixMultiply(matRotation, *RLGL.State.currentMatrix); -} - -// Multiply the current matrix by a scaling matrix -void rlScalef(float x, float y, float z) -{ - Matrix matScale = { - x, 0.0f, 0.0f, 0.0f, - 0.0f, y, 0.0f, 0.0f, - 0.0f, 0.0f, z, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - - // NOTE: We transpose matrix with multiplication order - *RLGL.State.currentMatrix = rlMatrixMultiply(matScale, *RLGL.State.currentMatrix); -} - -// Multiply the current matrix by another matrix -void rlMultMatrixf(const float *matf) -{ - // Matrix creation from array - Matrix mat = { matf[0], matf[4], matf[8], matf[12], - matf[1], matf[5], matf[9], matf[13], - matf[2], matf[6], matf[10], matf[14], - matf[3], matf[7], matf[11], matf[15] }; - - *RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, mat); -} - -// Multiply the current matrix by a perspective matrix generated by parameters -void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar) -{ - Matrix matFrustum = { 0 }; - - float rl = (float)(right - left); - float tb = (float)(top - bottom); - float fn = (float)(zfar - znear); - - matFrustum.m0 = ((float) znear*2.0f)/rl; - matFrustum.m1 = 0.0f; - matFrustum.m2 = 0.0f; - matFrustum.m3 = 0.0f; - - matFrustum.m4 = 0.0f; - matFrustum.m5 = ((float) znear*2.0f)/tb; - matFrustum.m6 = 0.0f; - matFrustum.m7 = 0.0f; - - matFrustum.m8 = ((float)right + (float)left)/rl; - matFrustum.m9 = ((float)top + (float)bottom)/tb; - matFrustum.m10 = -((float)zfar + (float)znear)/fn; - matFrustum.m11 = -1.0f; - - matFrustum.m12 = 0.0f; - matFrustum.m13 = 0.0f; - matFrustum.m14 = -((float)zfar*(float)znear*2.0f)/fn; - matFrustum.m15 = 0.0f; - - *RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, matFrustum); -} - -// Multiply the current matrix by an orthographic matrix generated by parameters -void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar) -{ - // NOTE: If left-right and top-botton values are equal it could create a division by zero, - // response to it is platform/compiler dependant - Matrix matOrtho = { 0 }; - - float rl = (float)(right - left); - float tb = (float)(top - bottom); - float fn = (float)(zfar - znear); - - matOrtho.m0 = 2.0f/rl; - matOrtho.m1 = 0.0f; - matOrtho.m2 = 0.0f; - matOrtho.m3 = 0.0f; - matOrtho.m4 = 0.0f; - matOrtho.m5 = 2.0f/tb; - matOrtho.m6 = 0.0f; - matOrtho.m7 = 0.0f; - matOrtho.m8 = 0.0f; - matOrtho.m9 = 0.0f; - matOrtho.m10 = -2.0f/fn; - matOrtho.m11 = 0.0f; - matOrtho.m12 = -((float)left + (float)right)/rl; - matOrtho.m13 = -((float)top + (float)bottom)/tb; - matOrtho.m14 = -((float)zfar + (float)znear)/fn; - matOrtho.m15 = 1.0f; - - *RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, matOrtho); -} -#endif - -// Set the viewport area (transformation from normalized device coordinates to window coordinates) -// NOTE: We store current viewport dimensions -void rlViewport(int x, int y, int width, int height) -{ - glViewport(x, y, width, height); -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - Vertex level operations -//---------------------------------------------------------------------------------- -#if defined(GRAPHICS_API_OPENGL_11) -// Fallback to OpenGL 1.1 function calls -//--------------------------------------- -void rlBegin(int mode) -{ - switch (mode) - { - case RL_LINES: glBegin(GL_LINES); break; - case RL_TRIANGLES: glBegin(GL_TRIANGLES); break; - case RL_QUADS: glBegin(GL_QUADS); break; - default: break; - } -} - -void rlEnd() { glEnd(); } -void rlVertex2i(int x, int y) { glVertex2i(x, y); } -void rlVertex2f(float x, float y) { glVertex2f(x, y); } -void rlVertex3f(float x, float y, float z) { glVertex3f(x, y, z); } -void rlTexCoord2f(float x, float y) { glTexCoord2f(x, y); } -void rlNormal3f(float x, float y, float z) { glNormal3f(x, y, z); } -void rlColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { glColor4ub(r, g, b, a); } -void rlColor3f(float x, float y, float z) { glColor3f(x, y, z); } -void rlColor4f(float x, float y, float z, float w) { glColor4f(x, y, z, w); } -#endif -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -// Initialize drawing mode (how to organize vertex) -void rlBegin(int mode) -{ - // Draw mode can be RL_LINES, RL_TRIANGLES and RL_QUADS - // NOTE: In all three cases, vertex are accumulated over default internal vertex buffer - if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode != mode) - { - if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount > 0) - { - // Make sure current RLGL.currentBatch->draws[i].vertexCount is aligned a multiple of 4, - // that way, following QUADS drawing will keep aligned with index processing - // It implies adding some extra alignment vertex at the end of the draw, - // those vertex are not processed but they are considered as an additional offset - // for the next set of vertex to be drawn - if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount : RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4); - else if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? 1 : (4 - (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4))); - else RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = 0; - - if (!rlCheckRenderBatchLimit(RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment)) - { - RLGL.State.vertexCounter += RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment; - RLGL.currentBatch->drawCounter++; - } - } - - if (RLGL.currentBatch->drawCounter >= RL_DEFAULT_BATCH_DRAWCALLS) rlDrawRenderBatch(RLGL.currentBatch); - - RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = mode; - RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount = 0; - RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = RLGL.State.defaultTextureId; - } -} - -// Finish vertex providing -void rlEnd(void) -{ - // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, - // as well as depth buffer bit-depth (16bit or 24bit or 32bit) - // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) - RLGL.currentBatch->currentDepth += (1.0f/20000.0f); -} - -// Define one vertex (position) -// NOTE: Vertex position data is the basic information required for drawing -void rlVertex3f(float x, float y, float z) -{ - float tx = x; - float ty = y; - float tz = z; - - // Transform provided vector if required - if (RLGL.State.transformRequired) - { - tx = RLGL.State.transform.m0*x + RLGL.State.transform.m4*y + RLGL.State.transform.m8*z + RLGL.State.transform.m12; - ty = RLGL.State.transform.m1*x + RLGL.State.transform.m5*y + RLGL.State.transform.m9*z + RLGL.State.transform.m13; - tz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z + RLGL.State.transform.m14; - } - - // WARNING: We can't break primitives when launching a new batch. - // RL_LINES comes in pairs, RL_TRIANGLES come in groups of 3 vertices and RL_QUADS come in groups of 4 vertices. - // We must check current draw.mode when a new vertex is required and finish the batch only if the draw.mode draw.vertexCount is %2, %3 or %4 - if (RLGL.State.vertexCounter > (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4 - 4)) - { - if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) && - (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%2 == 0)) - { - // Reached the maximum number of vertices for RL_LINES drawing - // Launch a draw call but keep current state for next vertices comming - // NOTE: We add +1 vertex to the check for security - rlCheckRenderBatchLimit(2 + 1); - } - else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) && - (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%3 == 0)) - { - rlCheckRenderBatchLimit(3 + 1); - } - else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_QUADS) && - (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4 == 0)) - { - rlCheckRenderBatchLimit(4 + 1); - } - } - - // Add vertices - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter] = tx; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 1] = ty; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 2] = tz; - - // Add current texcoord - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter] = RLGL.State.texcoordx; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter + 1] = RLGL.State.texcoordy; - - // TODO: Add current normal - // By default rlVertexBuffer type does not store normals - - // Add current color - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter] = RLGL.State.colorr; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 1] = RLGL.State.colorg; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 2] = RLGL.State.colorb; - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 3] = RLGL.State.colora; - - RLGL.State.vertexCounter++; - RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount++; -} - -// Define one vertex (position) -void rlVertex2f(float x, float y) -{ - rlVertex3f(x, y, RLGL.currentBatch->currentDepth); -} - -// Define one vertex (position) -void rlVertex2i(int x, int y) -{ - rlVertex3f((float)x, (float)y, RLGL.currentBatch->currentDepth); -} - -// Define one vertex (texture coordinate) -// NOTE: Texture coordinates are limited to QUADS only -void rlTexCoord2f(float x, float y) -{ - RLGL.State.texcoordx = x; - RLGL.State.texcoordy = y; -} - -// Define one vertex (normal) -// NOTE: Normals limited to TRIANGLES only? -void rlNormal3f(float x, float y, float z) -{ - RLGL.State.normalx = x; - RLGL.State.normaly = y; - RLGL.State.normalz = z; -} - -// Define one vertex (color) -void rlColor4ub(unsigned char x, unsigned char y, unsigned char z, unsigned char w) -{ - RLGL.State.colorr = x; - RLGL.State.colorg = y; - RLGL.State.colorb = z; - RLGL.State.colora = w; -} - -// Define one vertex (color) -void rlColor4f(float r, float g, float b, float a) -{ - rlColor4ub((unsigned char)(r*255), (unsigned char)(g*255), (unsigned char)(b*255), (unsigned char)(a*255)); -} - -// Define one vertex (color) -void rlColor3f(float x, float y, float z) -{ - rlColor4ub((unsigned char)(x*255), (unsigned char)(y*255), (unsigned char)(z*255), 255); -} - -#endif - -//-------------------------------------------------------------------------------------- -// Module Functions Definition - OpenGL style functions (common to 1.1, 3.3+, ES2) -//-------------------------------------------------------------------------------------- - -// Set current texture to use -void rlSetTexture(unsigned int id) -{ - if (id == 0) - { -#if defined(GRAPHICS_API_OPENGL_11) - rlDisableTexture(); -#else - // NOTE: If quads batch limit is reached, we force a draw call and next batch starts - if (RLGL.State.vertexCounter >= - RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4) - { - rlDrawRenderBatch(RLGL.currentBatch); - } -#endif - } - else - { -#if defined(GRAPHICS_API_OPENGL_11) - rlEnableTexture(id); -#else - if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId != id) - { - if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount > 0) - { - // Make sure current RLGL.currentBatch->draws[i].vertexCount is aligned a multiple of 4, - // that way, following QUADS drawing will keep aligned with index processing - // It implies adding some extra alignment vertex at the end of the draw, - // those vertex are not processed but they are considered as an additional offset - // for the next set of vertex to be drawn - if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount : RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4); - else if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? 1 : (4 - (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4))); - else RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = 0; - - if (!rlCheckRenderBatchLimit(RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment)) - { - RLGL.State.vertexCounter += RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment; - - RLGL.currentBatch->drawCounter++; - } - } - - if (RLGL.currentBatch->drawCounter >= RL_DEFAULT_BATCH_DRAWCALLS) rlDrawRenderBatch(RLGL.currentBatch); - - RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = id; - RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount = 0; - } -#endif - } -} - -// Select and active a texture slot -void rlActiveTextureSlot(int slot) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glActiveTexture(GL_TEXTURE0 + slot); -#endif -} - -// Enable texture -void rlEnableTexture(unsigned int id) -{ -#if defined(GRAPHICS_API_OPENGL_11) - glEnable(GL_TEXTURE_2D); -#endif - glBindTexture(GL_TEXTURE_2D, id); -} - -// Disable texture -void rlDisableTexture(void) -{ -#if defined(GRAPHICS_API_OPENGL_11) - glDisable(GL_TEXTURE_2D); -#endif - glBindTexture(GL_TEXTURE_2D, 0); -} - -// Enable texture cubemap -void rlEnableTextureCubemap(unsigned int id) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindTexture(GL_TEXTURE_CUBE_MAP, id); -#endif -} - -// Disable texture cubemap -void rlDisableTextureCubemap(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); -#endif -} - -// Set texture parameters (wrap mode/filter mode) -void rlTextureParameters(unsigned int id, int param, int value) -{ - glBindTexture(GL_TEXTURE_2D, id); - -#if !defined(GRAPHICS_API_OPENGL_11) - // Reset anisotropy filter, in case it was set - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); -#endif - - switch (param) - { - case RL_TEXTURE_WRAP_S: - case RL_TEXTURE_WRAP_T: - { - if (value == RL_TEXTURE_WRAP_MIRROR_CLAMP) - { -#if !defined(GRAPHICS_API_OPENGL_11) - if (RLGL.ExtSupported.texMirrorClamp) glTexParameteri(GL_TEXTURE_2D, param, value); - else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); -#endif - } - else glTexParameteri(GL_TEXTURE_2D, param, value); - - } break; - case RL_TEXTURE_MAG_FILTER: - case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break; - case RL_TEXTURE_FILTER_ANISOTROPIC: - { -#if !defined(GRAPHICS_API_OPENGL_11) - if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); - else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f) - { - TRACELOG(RL_LOG_WARNING, "GL: Maximum anisotropic filter level supported is %iX", id, (int)RLGL.ExtSupported.maxAnisotropyLevel); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); - } - else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); -#endif - } break; -#if defined(GRAPHICS_API_OPENGL_33) - case RL_TEXTURE_MIPMAP_BIAS_RATIO: glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, value/100.0f); -#endif - default: break; - } - - glBindTexture(GL_TEXTURE_2D, 0); -} - -// Set cubemap parameters (wrap mode/filter mode) -void rlCubemapParameters(unsigned int id, int param, int value) -{ -#if !defined(GRAPHICS_API_OPENGL_11) - glBindTexture(GL_TEXTURE_CUBE_MAP, id); - - // Reset anisotropy filter, in case it was set - glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); - - switch (param) - { - case RL_TEXTURE_WRAP_S: - case RL_TEXTURE_WRAP_T: - { - if (value == RL_TEXTURE_WRAP_MIRROR_CLAMP) - { - if (RLGL.ExtSupported.texMirrorClamp) glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); - else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); - } - else glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); - - } break; - case RL_TEXTURE_MAG_FILTER: - case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); break; - case RL_TEXTURE_FILTER_ANISOTROPIC: - { - if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); - else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f) - { - TRACELOG(RL_LOG_WARNING, "GL: Maximum anisotropic filter level supported is %iX", id, (int)RLGL.ExtSupported.maxAnisotropyLevel); - glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); - } - else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); - } break; -#if defined(GRAPHICS_API_OPENGL_33) - case RL_TEXTURE_MIPMAP_BIAS_RATIO: glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_LOD_BIAS, value/100.0f); -#endif - default: break; - } - - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); -#endif -} - -// Enable shader program -void rlEnableShader(unsigned int id) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) - glUseProgram(id); -#endif -} - -// Disable shader program -void rlDisableShader(void) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) - glUseProgram(0); -#endif -} - -// Enable rendering to texture (fbo) -void rlEnableFramebuffer(unsigned int id) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) - glBindFramebuffer(GL_FRAMEBUFFER, id); -#endif -} - -// Disable rendering to texture -void rlDisableFramebuffer(void) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) - glBindFramebuffer(GL_FRAMEBUFFER, 0); -#endif -} - -// Activate multiple draw color buffers -// NOTE: One color buffer is always active by default -void rlActiveDrawBuffers(int count) -{ -#if (defined(GRAPHICS_API_OPENGL_33) && defined(RLGL_RENDER_TEXTURES_HINT)) - // NOTE: Maximum number of draw buffers supported is implementation dependant, - // it can be queried with glGet*() but it must be at least 8 - //GLint maxDrawBuffers = 0; - //glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); - - if (count > 0) - { - if (count > 8) TRACELOG(LOG_WARNING, "GL: Max color buffers limited to 8"); - else - { - unsigned int buffers[8] = { - GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, - GL_COLOR_ATTACHMENT2, - GL_COLOR_ATTACHMENT3, - GL_COLOR_ATTACHMENT4, - GL_COLOR_ATTACHMENT5, - GL_COLOR_ATTACHMENT6, - GL_COLOR_ATTACHMENT7, - }; - - glDrawBuffers(count, buffers); - } - } - else TRACELOG(LOG_WARNING, "GL: One color buffer active by default"); -#endif -} - -//---------------------------------------------------------------------------------- -// General render state configuration -//---------------------------------------------------------------------------------- - -// Enable color blending -void rlEnableColorBlend(void) { glEnable(GL_BLEND); } - -// Disable color blending -void rlDisableColorBlend(void) { glDisable(GL_BLEND); } - -// Enable depth test -void rlEnableDepthTest(void) { glEnable(GL_DEPTH_TEST); } - -// Disable depth test -void rlDisableDepthTest(void) { glDisable(GL_DEPTH_TEST); } - -// Enable depth write -void rlEnableDepthMask(void) { glDepthMask(GL_TRUE); } - -// Disable depth write -void rlDisableDepthMask(void) { glDepthMask(GL_FALSE); } - -// Enable backface culling -void rlEnableBackfaceCulling(void) { glEnable(GL_CULL_FACE); } - -// Disable backface culling -void rlDisableBackfaceCulling(void) { glDisable(GL_CULL_FACE); } - -// Set face culling mode -void rlSetCullFace(int mode) -{ - switch (mode) - { - case RL_CULL_FACE_BACK: glCullFace(GL_BACK); break; - case RL_CULL_FACE_FRONT: glCullFace(GL_FRONT); break; - default: break; - } -} - -// Enable scissor test -void rlEnableScissorTest(void) { glEnable(GL_SCISSOR_TEST); } - -// Disable scissor test -void rlDisableScissorTest(void) { glDisable(GL_SCISSOR_TEST); } - -// Scissor test -void rlScissor(int x, int y, int width, int height) { glScissor(x, y, width, height); } - -// Enable wire mode -void rlEnableWireMode(void) -{ -#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - // NOTE: glPolygonMode() not available on OpenGL ES - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); -#endif -} - -// Disable wire mode -void rlDisableWireMode(void) -{ -#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - // NOTE: glPolygonMode() not available on OpenGL ES - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); -#endif -} - -// Set the line drawing width -void rlSetLineWidth(float width) { glLineWidth(width); } - -// Get the line drawing width -float rlGetLineWidth(void) -{ - float width = 0; - glGetFloatv(GL_LINE_WIDTH, &width); - return width; -} - -// Enable line aliasing -void rlEnableSmoothLines(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_11) - glEnable(GL_LINE_SMOOTH); -#endif -} - -// Disable line aliasing -void rlDisableSmoothLines(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_11) - glDisable(GL_LINE_SMOOTH); -#endif -} - -// Enable stereo rendering -void rlEnableStereoRender(void) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) - RLGL.State.stereoRender = true; -#endif -} - -// Disable stereo rendering -void rlDisableStereoRender(void) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) - RLGL.State.stereoRender = false; -#endif -} - -// Check if stereo render is enabled -bool rlIsStereoRenderEnabled(void) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) - return RLGL.State.stereoRender; -#else - return false; -#endif -} - -// Clear color buffer with color -void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - // Color values clamp to 0.0f(0) and 1.0f(255) - float cr = (float)r/255; - float cg = (float)g/255; - float cb = (float)b/255; - float ca = (float)a/255; - - glClearColor(cr, cg, cb, ca); -} - -// Clear used screen buffers (color and depth) -void rlClearScreenBuffers(void) -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers: Color and Depth (Depth is used for 3D) - //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Stencil buffer not used... -} - -// Check and log OpenGL error codes -void rlCheckErrors() -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - int check = 1; - while (check) - { - const GLenum err = glGetError(); - switch (err) - { - case GL_NO_ERROR: check = 0; break; - case 0x0500: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_ENUM"); break; - case 0x0501: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_VALUE"); break; - case 0x0502: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_OPERATION"); break; - case 0x0503: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_STACK_OVERFLOW"); break; - case 0x0504: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_STACK_UNDERFLOW"); break; - case 0x0505: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_OUT_OF_MEMORY"); break; - case 0x0506: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_FRAMEBUFFER_OPERATION"); break; - default: TRACELOG(RL_LOG_WARNING, "GL: Error detected: Unknown error code: %x", err); break; - } - } -#endif -} - -// Set blend mode -void rlSetBlendMode(int mode) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((RLGL.State.currentBlendMode != mode) || ((mode == RL_BLEND_CUSTOM || mode == RL_BLEND_CUSTOM_SEPARATE) && RLGL.State.glCustomBlendModeModified)) - { - rlDrawRenderBatch(RLGL.currentBatch); - - switch (mode) - { - case RL_BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; - case RL_BLEND_ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; - case RL_BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; - case RL_BLEND_ADD_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; - case RL_BLEND_SUBTRACT_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_SUBTRACT); break; - case RL_BLEND_ALPHA_PREMULTIPLY: glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; - case RL_BLEND_CUSTOM: - { - // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactors() - glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation); - - } break; - case RL_BLEND_CUSTOM_SEPARATE: - { - // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactorsSeparate() - glBlendFuncSeparate(RLGL.State.glBlendSrcFactorRGB, RLGL.State.glBlendDestFactorRGB, RLGL.State.glBlendSrcFactorAlpha, RLGL.State.glBlendDestFactorAlpha); - glBlendEquationSeparate(RLGL.State.glBlendEquationRGB, RLGL.State.glBlendEquationAlpha); - - } break; - default: break; - } - - RLGL.State.currentBlendMode = mode; - RLGL.State.glCustomBlendModeModified = false; - } -#endif -} - -// Set blending mode factor and equation -void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((RLGL.State.glBlendSrcFactor != glSrcFactor) || - (RLGL.State.glBlendDstFactor != glDstFactor) || - (RLGL.State.glBlendEquation != glEquation)) - { - RLGL.State.glBlendSrcFactor = glSrcFactor; - RLGL.State.glBlendDstFactor = glDstFactor; - RLGL.State.glBlendEquation = glEquation; - - RLGL.State.glCustomBlendModeModified = true; - } -#endif -} - -// Set blending mode factor and equation separately for RGB and alpha -void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((RLGL.State.glBlendSrcFactorRGB != glSrcRGB) || - (RLGL.State.glBlendDestFactorRGB != glDstRGB) || - (RLGL.State.glBlendSrcFactorAlpha != glSrcAlpha) || - (RLGL.State.glBlendDestFactorAlpha != glDstAlpha) || - (RLGL.State.glBlendEquationRGB != glEqRGB) || - (RLGL.State.glBlendEquationAlpha != glEqAlpha)) - { - RLGL.State.glBlendSrcFactorRGB = glSrcRGB; - RLGL.State.glBlendDestFactorRGB = glDstRGB; - RLGL.State.glBlendSrcFactorAlpha = glSrcAlpha; - RLGL.State.glBlendDestFactorAlpha = glDstAlpha; - RLGL.State.glBlendEquationRGB = glEqRGB; - RLGL.State.glBlendEquationAlpha = glEqAlpha; - - RLGL.State.glCustomBlendModeModified = true; - } -#endif -} - -//---------------------------------------------------------------------------------- -// Module Functions Definition - OpenGL Debug -//---------------------------------------------------------------------------------- -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43) -static void GLAPIENTRY rlDebugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) -{ - // Ignore non-significant error/warning codes (NVidia drivers) - // NOTE: Here there are the details with a sample output: - // - #131169 - Framebuffer detailed info: The driver allocated storage for renderbuffer 2. (severity: low) - // - #131185 - Buffer detailed info: Buffer object 1 (bound to GL_ELEMENT_ARRAY_BUFFER_ARB, usage hint is GL_ENUM_88e4) - // will use VIDEO memory as the source for buffer object operations. (severity: low) - // - #131218 - Program/shader state performance warning: Vertex shader in program 7 is being recompiled based on GL state. (severity: medium) - // - #131204 - Texture state usage warning: The texture object (0) bound to texture image unit 0 does not have - // a defined base level and cannot be used for texture mapping. (severity: low) - if ((id == 131169) || (id == 131185) || (id == 131218) || (id == 131204)) return; - - const char *msgSource = NULL; - switch (source) - { - case GL_DEBUG_SOURCE_API: msgSource = "API"; break; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM: msgSource = "WINDOW_SYSTEM"; break; - case GL_DEBUG_SOURCE_SHADER_COMPILER: msgSource = "SHADER_COMPILER"; break; - case GL_DEBUG_SOURCE_THIRD_PARTY: msgSource = "THIRD_PARTY"; break; - case GL_DEBUG_SOURCE_APPLICATION: msgSource = "APPLICATION"; break; - case GL_DEBUG_SOURCE_OTHER: msgSource = "OTHER"; break; - default: break; - } - - const char *msgType = NULL; - switch (type) - { - case GL_DEBUG_TYPE_ERROR: msgType = "ERROR"; break; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: msgType = "DEPRECATED_BEHAVIOR"; break; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: msgType = "UNDEFINED_BEHAVIOR"; break; - case GL_DEBUG_TYPE_PORTABILITY: msgType = "PORTABILITY"; break; - case GL_DEBUG_TYPE_PERFORMANCE: msgType = "PERFORMANCE"; break; - case GL_DEBUG_TYPE_MARKER: msgType = "MARKER"; break; - case GL_DEBUG_TYPE_PUSH_GROUP: msgType = "PUSH_GROUP"; break; - case GL_DEBUG_TYPE_POP_GROUP: msgType = "POP_GROUP"; break; - case GL_DEBUG_TYPE_OTHER: msgType = "OTHER"; break; - default: break; - } - - const char *msgSeverity = "DEFAULT"; - switch (severity) - { - case GL_DEBUG_SEVERITY_LOW: msgSeverity = "LOW"; break; - case GL_DEBUG_SEVERITY_MEDIUM: msgSeverity = "MEDIUM"; break; - case GL_DEBUG_SEVERITY_HIGH: msgSeverity = "HIGH"; break; - case GL_DEBUG_SEVERITY_NOTIFICATION: msgSeverity = "NOTIFICATION"; break; - default: break; - } - - TRACELOG(LOG_WARNING, "GL: OpenGL debug message: %s", message); - TRACELOG(LOG_WARNING, " > Type: %s", msgType); - TRACELOG(LOG_WARNING, " > Source = %s", msgSource); - TRACELOG(LOG_WARNING, " > Severity = %s", msgSeverity); -} -#endif - -//---------------------------------------------------------------------------------- -// Module Functions Definition - rlgl functionality -//---------------------------------------------------------------------------------- - -// Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states -void rlglInit(int width, int height) -{ - // Enable OpenGL debug context if required -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43) - if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL)) - { - glDebugMessageCallback(rlDebugMessageCallback, 0); - // glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_HIGH, 0, 0, GL_TRUE); // TODO: Filter message - - // Debug context options: - // - GL_DEBUG_OUTPUT - Faster version but not useful for breakpoints - // - GL_DEBUG_OUTPUT_SYNCHRONUS - Callback is in sync with errors, so a breakpoint can be placed on the callback in order to get a stacktrace for the GL error - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - } -#endif - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Init default white texture - unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) - RLGL.State.defaultTextureId = rlLoadTexture(pixels, 1, 1, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1); - - if (RLGL.State.defaultTextureId != 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture loaded successfully", RLGL.State.defaultTextureId); - else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load default texture"); - - // Init default Shader (customized for GL 3.3 and ES2) - // Loaded: RLGL.State.defaultShaderId + RLGL.State.defaultShaderLocs - rlLoadShaderDefault(); - RLGL.State.currentShaderId = RLGL.State.defaultShaderId; - RLGL.State.currentShaderLocs = RLGL.State.defaultShaderLocs; - - // Init default vertex arrays buffers - RLGL.defaultBatch = rlLoadRenderBatch(RL_DEFAULT_BATCH_BUFFERS, RL_DEFAULT_BATCH_BUFFER_ELEMENTS); - RLGL.currentBatch = &RLGL.defaultBatch; - - // Init stack matrices (emulating OpenGL 1.1) - for (int i = 0; i < RL_MAX_MATRIX_STACK_SIZE; i++) RLGL.State.stack[i] = rlMatrixIdentity(); - - // Init internal matrices - RLGL.State.transform = rlMatrixIdentity(); - RLGL.State.projection = rlMatrixIdentity(); - RLGL.State.modelview = rlMatrixIdentity(); - RLGL.State.currentMatrix = &RLGL.State.modelview; -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 - - // Initialize OpenGL default states - //---------------------------------------------------------- - // Init state: Depth test - glDepthFunc(GL_LEQUAL); // Type of depth testing to apply - glDisable(GL_DEPTH_TEST); // Disable depth testing for 2D (only used for 3D) - - // Init state: Blending mode - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed) - glEnable(GL_BLEND); // Enable color blending (required to work with transparencies) - - // Init state: Culling - // NOTE: All shapes/models triangles are drawn CCW - glCullFace(GL_BACK); // Cull the back face (default) - glFrontFace(GL_CCW); // Front face are defined counter clockwise (default) - glEnable(GL_CULL_FACE); // Enable backface culling - - // Init state: Cubemap seamless -#if defined(GRAPHICS_API_OPENGL_33) - glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Seamless cubemaps (not supported on OpenGL ES 2.0) -#endif - -#if defined(GRAPHICS_API_OPENGL_11) - // Init state: Color hints (deprecated in OpenGL 3.0+) - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation - glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) -#endif - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Store screen size into global variables - RLGL.State.framebufferWidth = width; - RLGL.State.framebufferHeight = height; - - TRACELOG(RL_LOG_INFO, "RLGL: Default OpenGL state initialized successfully"); - //---------------------------------------------------------- -#endif - - // Init state: Color/Depth buffers clear - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black) - glClearDepth(1.0f); // Set clear depth value (default) - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers (depth buffer required for 3D) -} - -// Vertex Buffer Object deinitialization (memory free) -void rlglClose(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - rlUnloadRenderBatch(RLGL.defaultBatch); - - rlUnloadShaderDefault(); // Unload default shader - - glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture - TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId); -#endif -} - -// Load OpenGL extensions -// NOTE: External loader function must be provided -void rlLoadExtensions(void *loader) -{ -#if defined(GRAPHICS_API_OPENGL_33) // Also defined for GRAPHICS_API_OPENGL_21 - // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions (and lower versions) - if (gladLoadGL((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); - else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); - - // Get number of supported extensions - GLint numExt = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); - TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt); - -#if defined(RLGL_SHOW_GL_DETAILS_INFO) - // Get supported extensions list - // WARNING: glGetStringi() not available on OpenGL 2.1 - TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:"); - for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", glGetStringi(GL_EXTENSIONS, i)); -#endif - -#if defined(GRAPHICS_API_OPENGL_21) - // Register supported extensions flags - // Optional OpenGL 2.1 extensions - RLGL.ExtSupported.vao = GLAD_GL_ARB_vertex_array_object; - RLGL.ExtSupported.instancing = (GLAD_GL_EXT_draw_instanced && GLAD_GL_ARB_instanced_arrays); - RLGL.ExtSupported.texNPOT = GLAD_GL_ARB_texture_non_power_of_two; - RLGL.ExtSupported.texFloat32 = GLAD_GL_ARB_texture_float; - RLGL.ExtSupported.texDepth = GLAD_GL_ARB_depth_texture; - RLGL.ExtSupported.maxDepthBits = 32; - RLGL.ExtSupported.texAnisoFilter = GLAD_GL_EXT_texture_filter_anisotropic; - RLGL.ExtSupported.texMirrorClamp = GLAD_GL_EXT_texture_mirror_clamp; -#else - // Register supported extensions flags - // OpenGL 3.3 extensions supported by default (core) - RLGL.ExtSupported.vao = true; - RLGL.ExtSupported.instancing = true; - RLGL.ExtSupported.texNPOT = true; - RLGL.ExtSupported.texFloat32 = true; - RLGL.ExtSupported.texDepth = true; - RLGL.ExtSupported.maxDepthBits = 32; - RLGL.ExtSupported.texAnisoFilter = true; - RLGL.ExtSupported.texMirrorClamp = true; -#endif - - // Optional OpenGL 3.3 extensions - RLGL.ExtSupported.texCompASTC = GLAD_GL_KHR_texture_compression_astc_hdr && GLAD_GL_KHR_texture_compression_astc_ldr; - RLGL.ExtSupported.texCompDXT = GLAD_GL_EXT_texture_compression_s3tc; // Texture compression: DXT - RLGL.ExtSupported.texCompETC2 = GLAD_GL_ARB_ES3_compatibility; // Texture compression: ETC2/EAC - #if defined(GRAPHICS_API_OPENGL_43) - RLGL.ExtSupported.computeShader = GLAD_GL_ARB_compute_shader; - RLGL.ExtSupported.ssbo = GLAD_GL_ARB_shader_storage_buffer_object; - #endif - -#endif // GRAPHICS_API_OPENGL_33 - -#if defined(GRAPHICS_API_OPENGL_ES2) - - #if defined(PLATFORM_DESKTOP) - if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); - else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES2.0 loaded successfully"); - #endif - - // Get supported extensions list - GLint numExt = 0; - const char **extList = RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) - const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string - - // NOTE: We have to duplicate string because glGetString() returns a const string - int size = strlen(extensions) + 1; // Get extensions string size in bytes - char *extensionsDup = (char *)RL_CALLOC(size, sizeof(char)); - strcpy(extensionsDup, extensions); - extList[numExt] = extensionsDup; - - for (int i = 0; i < size; i++) - { - if (extensionsDup[i] == ' ') - { - extensionsDup[i] = '\0'; - numExt++; - extList[numExt] = &extensionsDup[i + 1]; - } - } - - TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt); - -#if defined(RLGL_SHOW_GL_DETAILS_INFO) - TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:"); - for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", extList[i]); -#endif - - // Check required extensions - for (int i = 0; i < numExt; i++) - { - // Check VAO support - // NOTE: Only check on OpenGL ES, OpenGL 3.3 has VAO support as core feature - if (strcmp(extList[i], (const char *)"GL_OES_vertex_array_object") == 0) - { - // The extension is supported by our hardware and driver, try to get related functions pointers - // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... - glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)((rlglLoadProc)loader)("glGenVertexArraysOES"); - glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)((rlglLoadProc)loader)("glBindVertexArrayOES"); - glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)((rlglLoadProc)loader)("glDeleteVertexArraysOES"); - //glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)loader("glIsVertexArrayOES"); // NOTE: Fails in WebGL, omitted - - if ((glGenVertexArrays != NULL) && (glBindVertexArray != NULL) && (glDeleteVertexArrays != NULL)) RLGL.ExtSupported.vao = true; - } - - // Check instanced rendering support - if (strcmp(extList[i], (const char *)"GL_ANGLE_instanced_arrays") == 0) // Web ANGLE - { - glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedANGLE"); - glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedANGLE"); - glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISOREXTPROC)((rlglLoadProc)loader)("glVertexAttribDivisorANGLE"); - - if ((glDrawArraysInstanced != NULL) && (glDrawElementsInstanced != NULL) && (glVertexAttribDivisor != NULL)) RLGL.ExtSupported.instancing = true; - } - else - { - if ((strcmp(extList[i], (const char *)"GL_EXT_draw_instanced") == 0) && // Standard EXT - (strcmp(extList[i], (const char *)"GL_EXT_instanced_arrays") == 0)) - { - glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedEXT"); - glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedEXT"); - glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISOREXTPROC)((rlglLoadProc)loader)("glVertexAttribDivisorEXT"); - - if ((glDrawArraysInstanced != NULL) && (glDrawElementsInstanced != NULL) && (glVertexAttribDivisor != NULL)) RLGL.ExtSupported.instancing = true; - } - } - - // Check NPOT textures support - // NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature - if (strcmp(extList[i], (const char *)"GL_OES_texture_npot") == 0) RLGL.ExtSupported.texNPOT = true; - - // Check texture float support - if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) RLGL.ExtSupported.texFloat32 = true; - - // Check depth texture support - if (strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) RLGL.ExtSupported.texDepth = true; - if (strcmp(extList[i], (const char *)"GL_WEBGL_depth_texture") == 0) RLGL.ExtSupported.texDepthWebGL = true; // WebGL requires unsized internal format - if (RLGL.ExtSupported.texDepthWebGL) RLGL.ExtSupported.texDepth = true; - - if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) RLGL.ExtSupported.maxDepthBits = 24; // Not available on WebGL - if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) RLGL.ExtSupported.maxDepthBits = 32; // Not available on WebGL - - // Check texture compression support: DXT - if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) || - (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_s3tc") == 0) || - (strcmp(extList[i], (const char *)"GL_WEBKIT_WEBGL_compressed_texture_s3tc") == 0)) RLGL.ExtSupported.texCompDXT = true; - - // Check texture compression support: ETC1 - if ((strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) || - (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_etc1") == 0)) RLGL.ExtSupported.texCompETC1 = true; - - // Check texture compression support: ETC2/EAC - if (strcmp(extList[i], (const char *)"GL_ARB_ES3_compatibility") == 0) RLGL.ExtSupported.texCompETC2 = true; - - // Check texture compression support: PVR - if (strcmp(extList[i], (const char *)"GL_IMG_texture_compression_pvrtc") == 0) RLGL.ExtSupported.texCompPVRT = true; - - // Check texture compression support: ASTC - if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) RLGL.ExtSupported.texCompASTC = true; - - // Check anisotropic texture filter support - if (strcmp(extList[i], (const char *)"GL_EXT_texture_filter_anisotropic") == 0) RLGL.ExtSupported.texAnisoFilter = true; - - // Check clamp mirror wrap mode support - if (strcmp(extList[i], (const char *)"GL_EXT_texture_mirror_clamp") == 0) RLGL.ExtSupported.texMirrorClamp = true; - } - - // Free extensions pointers - RL_FREE(extList); - RL_FREE(extensionsDup); // Duplicated string must be deallocated -#endif // GRAPHICS_API_OPENGL_ES2 - - // Check OpenGL information and capabilities - //------------------------------------------------------------------------------ - // Show current OpenGL and GLSL version - TRACELOG(RL_LOG_INFO, "GL: OpenGL device information:"); - TRACELOG(RL_LOG_INFO, " > Vendor: %s", glGetString(GL_VENDOR)); - TRACELOG(RL_LOG_INFO, " > Renderer: %s", glGetString(GL_RENDERER)); - TRACELOG(RL_LOG_INFO, " > Version: %s", glGetString(GL_VERSION)); - TRACELOG(RL_LOG_INFO, " > GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: Anisotropy levels capability is an extension - #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT - #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF - #endif - glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &RLGL.ExtSupported.maxAnisotropyLevel); - -#if defined(RLGL_SHOW_GL_DETAILS_INFO) - // Show some OpenGL GPU capabilities - TRACELOG(RL_LOG_INFO, "GL: OpenGL capabilities:"); - GLint capability = 0; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &capability); - TRACELOG(RL_LOG_INFO, " GL_MAX_TEXTURE_SIZE: %i", capability); - glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &capability); - TRACELOG(RL_LOG_INFO, " GL_MAX_CUBE_MAP_TEXTURE_SIZE: %i", capability); - glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &capability); - TRACELOG(RL_LOG_INFO, " GL_MAX_TEXTURE_IMAGE_UNITS: %i", capability); - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &capability); - TRACELOG(RL_LOG_INFO, " GL_MAX_VERTEX_ATTRIBS: %i", capability); - #if !defined(GRAPHICS_API_OPENGL_ES2) - glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &capability); - TRACELOG(RL_LOG_INFO, " GL_MAX_UNIFORM_BLOCK_SIZE: %i", capability); - glGetIntegerv(GL_MAX_DRAW_BUFFERS, &capability); - TRACELOG(RL_LOG_INFO, " GL_MAX_DRAW_BUFFERS: %i", capability); - if (RLGL.ExtSupported.texAnisoFilter) TRACELOG(RL_LOG_INFO, " GL_MAX_TEXTURE_MAX_ANISOTROPY: %.0f", RLGL.ExtSupported.maxAnisotropyLevel); - #endif - glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &capability); - TRACELOG(RL_LOG_INFO, " GL_NUM_COMPRESSED_TEXTURE_FORMATS: %i", capability); - GLint *compFormats = (GLint *)RL_CALLOC(capability, sizeof(GLint)); - glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, compFormats); - for (int i = 0; i < capability; i++) TRACELOG(RL_LOG_INFO, " %s", rlGetCompressedFormatName(compFormats[i])); - RL_FREE(compFormats); - -#if defined(GRAPHICS_API_OPENGL_43) - glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &capability); - TRACELOG(RL_LOG_INFO, " GL_MAX_VERTEX_ATTRIB_BINDINGS: %i", capability); - glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &capability); - TRACELOG(RL_LOG_INFO, " GL_MAX_UNIFORM_LOCATIONS: %i", capability); -#endif // GRAPHICS_API_OPENGL_43 -#else // RLGL_SHOW_GL_DETAILS_INFO - - // Show some basic info about GL supported features - if (RLGL.ExtSupported.vao) TRACELOG(RL_LOG_INFO, "GL: VAO extension detected, VAO functions loaded successfully"); - else TRACELOG(RL_LOG_WARNING, "GL: VAO extension not found, VAO not supported"); - if (RLGL.ExtSupported.texNPOT) TRACELOG(RL_LOG_INFO, "GL: NPOT textures extension detected, full NPOT textures supported"); - else TRACELOG(RL_LOG_WARNING, "GL: NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); - if (RLGL.ExtSupported.texCompDXT) TRACELOG(RL_LOG_INFO, "GL: DXT compressed textures supported"); - if (RLGL.ExtSupported.texCompETC1) TRACELOG(RL_LOG_INFO, "GL: ETC1 compressed textures supported"); - if (RLGL.ExtSupported.texCompETC2) TRACELOG(RL_LOG_INFO, "GL: ETC2/EAC compressed textures supported"); - if (RLGL.ExtSupported.texCompPVRT) TRACELOG(RL_LOG_INFO, "GL: PVRT compressed textures supported"); - if (RLGL.ExtSupported.texCompASTC) TRACELOG(RL_LOG_INFO, "GL: ASTC compressed textures supported"); - if (RLGL.ExtSupported.computeShader) TRACELOG(RL_LOG_INFO, "GL: Compute shaders supported"); - if (RLGL.ExtSupported.ssbo) TRACELOG(RL_LOG_INFO, "GL: Shader storage buffer objects supported"); -#endif // RLGL_SHOW_GL_DETAILS_INFO - -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -} - -// Get current OpenGL version -int rlGetVersion(void) -{ - int glVersion = 0; -#if defined(GRAPHICS_API_OPENGL_11) - glVersion = RL_OPENGL_11; -#endif -#if defined(GRAPHICS_API_OPENGL_21) - glVersion = RL_OPENGL_21; -#elif defined(GRAPHICS_API_OPENGL_33) - glVersion = RL_OPENGL_33; -#endif -#if defined(GRAPHICS_API_OPENGL_43) - glVersion = RL_OPENGL_43; -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) - glVersion = RL_OPENGL_ES_20; -#endif - return glVersion; -} - -// Set current framebuffer width -void rlSetFramebufferWidth(int width) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - RLGL.State.framebufferWidth = width; -#endif -} - -// Set current framebuffer height -void rlSetFramebufferHeight(int height) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - RLGL.State.framebufferHeight = height; -#endif -} - -// Get default framebuffer width -int rlGetFramebufferWidth(void) -{ - int width = 0; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - width = RLGL.State.framebufferWidth; -#endif - return width; -} - -// Get default framebuffer height -int rlGetFramebufferHeight(void) -{ - int height = 0; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - height = RLGL.State.framebufferHeight; -#endif - return height; -} - -// Get default internal texture (white texture) -// NOTE: Default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 -unsigned int rlGetTextureIdDefault(void) -{ - unsigned int id = 0; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - id = RLGL.State.defaultTextureId; -#endif - return id; -} - -// Get default shader id -unsigned int rlGetShaderIdDefault(void) -{ - unsigned int id = 0; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - id = RLGL.State.defaultShaderId; -#endif - return id; -} - -// Get default shader locs -int *rlGetShaderLocsDefault(void) -{ - int *locs = NULL; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - locs = RLGL.State.defaultShaderLocs; -#endif - return locs; -} - -// Render batch management -//------------------------------------------------------------------------------------------------ -// Load render batch -rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements) -{ - rlRenderBatch batch = { 0 }; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Initialize CPU (RAM) vertex buffers (position, texcoord, color data and indexes) - //-------------------------------------------------------------------------------------------- - batch.vertexBuffer = (rlVertexBuffer *)RL_MALLOC(numBuffers*sizeof(rlVertexBuffer)); - - for (int i = 0; i < numBuffers; i++) - { - batch.vertexBuffer[i].elementCount = bufferElements; - - batch.vertexBuffer[i].vertices = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float)); // 3 float by vertex, 4 vertex by quad - batch.vertexBuffer[i].texcoords = (float *)RL_MALLOC(bufferElements*2*4*sizeof(float)); // 2 float by texcoord, 4 texcoord by quad - batch.vertexBuffer[i].colors = (unsigned char *)RL_MALLOC(bufferElements*4*4*sizeof(unsigned char)); // 4 float by color, 4 colors by quad -#if defined(GRAPHICS_API_OPENGL_33) - batch.vertexBuffer[i].indices = (unsigned int *)RL_MALLOC(bufferElements*6*sizeof(unsigned int)); // 6 int by quad (indices) -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) - batch.vertexBuffer[i].indices = (unsigned short *)RL_MALLOC(bufferElements*6*sizeof(unsigned short)); // 6 int by quad (indices) -#endif - - for (int j = 0; j < (3*4*bufferElements); j++) batch.vertexBuffer[i].vertices[j] = 0.0f; - for (int j = 0; j < (2*4*bufferElements); j++) batch.vertexBuffer[i].texcoords[j] = 0.0f; - for (int j = 0; j < (4*4*bufferElements); j++) batch.vertexBuffer[i].colors[j] = 0; - - int k = 0; - - // Indices can be initialized right now - for (int j = 0; j < (6*bufferElements); j += 6) - { - batch.vertexBuffer[i].indices[j] = 4*k; - batch.vertexBuffer[i].indices[j + 1] = 4*k + 1; - batch.vertexBuffer[i].indices[j + 2] = 4*k + 2; - batch.vertexBuffer[i].indices[j + 3] = 4*k; - batch.vertexBuffer[i].indices[j + 4] = 4*k + 2; - batch.vertexBuffer[i].indices[j + 5] = 4*k + 3; - - k++; - } - - RLGL.State.vertexCounter = 0; - } - - TRACELOG(RL_LOG_INFO, "RLGL: Render batch vertex buffers loaded successfully in RAM (CPU)"); - //-------------------------------------------------------------------------------------------- - - // Upload to GPU (VRAM) vertex data and initialize VAOs/VBOs - //-------------------------------------------------------------------------------------------- - for (int i = 0; i < numBuffers; i++) - { - if (RLGL.ExtSupported.vao) - { - // Initialize Quads VAO - glGenVertexArrays(1, &batch.vertexBuffer[i].vaoId); - glBindVertexArray(batch.vertexBuffer[i].vaoId); - } - - // Quads - Vertex buffers binding and attributes enable - // Vertex position buffer (shader-location = 0) - glGenBuffers(1, &batch.vertexBuffer[i].vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[0]); - glBufferData(GL_ARRAY_BUFFER, bufferElements*3*4*sizeof(float), batch.vertexBuffer[i].vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION]); - glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - - // Vertex texcoord buffer (shader-location = 1) - glGenBuffers(1, &batch.vertexBuffer[i].vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[1]); - glBufferData(GL_ARRAY_BUFFER, bufferElements*2*4*sizeof(float), batch.vertexBuffer[i].texcoords, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01]); - glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); - - // Vertex color buffer (shader-location = 3) - glGenBuffers(1, &batch.vertexBuffer[i].vboId[2]); - glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[2]); - glBufferData(GL_ARRAY_BUFFER, bufferElements*4*4*sizeof(unsigned char), batch.vertexBuffer[i].colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR]); - glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - - // Fill index buffer - glGenBuffers(1, &batch.vertexBuffer[i].vboId[3]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[3]); -#if defined(GRAPHICS_API_OPENGL_33) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(int), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(short), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); -#endif - } - - TRACELOG(RL_LOG_INFO, "RLGL: Render batch vertex buffers loaded successfully in VRAM (GPU)"); - - // Unbind the current VAO - if (RLGL.ExtSupported.vao) glBindVertexArray(0); - //-------------------------------------------------------------------------------------------- - - // Init draw calls tracking system - //-------------------------------------------------------------------------------------------- - batch.draws = (rlDrawCall *)RL_MALLOC(RL_DEFAULT_BATCH_DRAWCALLS*sizeof(rlDrawCall)); - - for (int i = 0; i < RL_DEFAULT_BATCH_DRAWCALLS; i++) - { - batch.draws[i].mode = RL_QUADS; - batch.draws[i].vertexCount = 0; - batch.draws[i].vertexAlignment = 0; - //batch.draws[i].vaoId = 0; - //batch.draws[i].shaderId = 0; - batch.draws[i].textureId = RLGL.State.defaultTextureId; - //batch.draws[i].RLGL.State.projection = rlMatrixIdentity(); - //batch.draws[i].RLGL.State.modelview = rlMatrixIdentity(); - } - - batch.bufferCount = numBuffers; // Record buffer count - batch.drawCounter = 1; // Reset draws counter - batch.currentDepth = -1.0f; // Reset depth value - //-------------------------------------------------------------------------------------------- -#endif - - return batch; -} - -// Unload default internal buffers vertex data from CPU and GPU -void rlUnloadRenderBatch(rlRenderBatch batch) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Unbind everything - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - // Unload all vertex buffers data - for (int i = 0; i < batch.bufferCount; i++) - { - // Unbind VAO attribs data - if (RLGL.ExtSupported.vao) - { - glBindVertexArray(batch.vertexBuffer[i].vaoId); - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(2); - glDisableVertexAttribArray(3); - glBindVertexArray(0); - } - - // Delete VBOs from GPU (VRAM) - glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[0]); - glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[1]); - glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[2]); - glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[3]); - - // Delete VAOs from GPU (VRAM) - if (RLGL.ExtSupported.vao) glDeleteVertexArrays(1, &batch.vertexBuffer[i].vaoId); - - // Free vertex arrays memory from CPU (RAM) - RL_FREE(batch.vertexBuffer[i].vertices); - RL_FREE(batch.vertexBuffer[i].texcoords); - RL_FREE(batch.vertexBuffer[i].colors); - RL_FREE(batch.vertexBuffer[i].indices); - } - - // Unload arrays - RL_FREE(batch.vertexBuffer); - RL_FREE(batch.draws); -#endif -} - -// Draw render batch -// NOTE: We require a pointer to reset batch and increase current buffer (multi-buffer) -void rlDrawRenderBatch(rlRenderBatch *batch) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Update batch vertex buffers - //------------------------------------------------------------------------------------------------------------ - // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) - // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) - if (RLGL.State.vertexCounter > 0) - { - // Activate elements VAO - if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); - - // Vertex positions buffer - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); - glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*3*sizeof(float), batch->vertexBuffer[batch->currentBuffer].vertices); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].vertices, GL_DYNAMIC_DRAW); // Update all buffer - - // Texture coordinates buffer - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); - glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*2*sizeof(float), batch->vertexBuffer[batch->currentBuffer].texcoords); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].texcoords, GL_DYNAMIC_DRAW); // Update all buffer - - // Colors buffer - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); - glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*4*sizeof(unsigned char), batch->vertexBuffer[batch->currentBuffer].colors); - //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].colors, GL_DYNAMIC_DRAW); // Update all buffer - - // NOTE: glMapBuffer() causes sync issue. - // If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job. - // To avoid waiting (idle), you can call first glBufferData() with NULL pointer before glMapBuffer(). - // If you do that, the previous data in PBO will be discarded and glMapBuffer() returns a new - // allocated pointer immediately even if GPU is still working with the previous data. - - // Another option: map the buffer object into client's memory - // Probably this code could be moved somewhere else... - // batch->vertexBuffer[batch->currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); - // if (batch->vertexBuffer[batch->currentBuffer].vertices) - // { - // Update vertex data - // } - // glUnmapBuffer(GL_ARRAY_BUFFER); - - // Unbind the current VAO - if (RLGL.ExtSupported.vao) glBindVertexArray(0); - } - //------------------------------------------------------------------------------------------------------------ - - // Draw batch vertex buffers (considering VR stereo if required) - //------------------------------------------------------------------------------------------------------------ - Matrix matProjection = RLGL.State.projection; - Matrix matModelView = RLGL.State.modelview; - - int eyeCount = 1; - if (RLGL.State.stereoRender) eyeCount = 2; - - for (int eye = 0; eye < eyeCount; eye++) - { - if (eyeCount == 2) - { - // Setup current eye viewport (half screen width) - rlViewport(eye*RLGL.State.framebufferWidth/2, 0, RLGL.State.framebufferWidth/2, RLGL.State.framebufferHeight); - - // Set current eye view offset to modelview matrix - rlSetMatrixModelview(rlMatrixMultiply(matModelView, RLGL.State.viewOffsetStereo[eye])); - // Set current eye projection matrix - rlSetMatrixProjection(RLGL.State.projectionStereo[eye]); - } - - // Draw buffers - if (RLGL.State.vertexCounter > 0) - { - // Set current shader and upload current MVP matrix - glUseProgram(RLGL.State.currentShaderId); - - // Create modelview-projection matrix and upload to shader - Matrix matMVP = rlMatrixMultiply(RLGL.State.modelview, RLGL.State.projection); - float matMVPfloat[16] = { - matMVP.m0, matMVP.m1, matMVP.m2, matMVP.m3, - matMVP.m4, matMVP.m5, matMVP.m6, matMVP.m7, - matMVP.m8, matMVP.m9, matMVP.m10, matMVP.m11, - matMVP.m12, matMVP.m13, matMVP.m14, matMVP.m15 - }; - glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_MVP], 1, false, matMVPfloat); - - if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); - else - { - // Bind vertex attrib: position (shader-location = 0) - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); - glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION]); - - // Bind vertex attrib: texcoord (shader-location = 1) - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); - glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01]); - - // Bind vertex attrib: color (shader-location = 3) - glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); - glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR]); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[3]); - } - - // Setup some default shader values - glUniform4f(RLGL.State.currentShaderLocs[RL_SHADER_LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); - glUniform1i(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MAP_DIFFUSE], 0); // Active default sampler2D: texture0 - - // Activate additional sampler textures - // Those additional textures will be common for all draw calls of the batch - for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) - { - if (RLGL.State.activeTextureId[i] > 0) - { - glActiveTexture(GL_TEXTURE0 + 1 + i); - glBindTexture(GL_TEXTURE_2D, RLGL.State.activeTextureId[i]); - } - } - - // Activate default sampler2D texture0 (one texture is always active for default batch shader) - // NOTE: Batch system accumulates calls by texture0 changes, additional textures are enabled for all the draw calls - glActiveTexture(GL_TEXTURE0); - - for (int i = 0, vertexOffset = 0; i < batch->drawCounter; i++) - { - // Bind current draw call texture, activated as GL_TEXTURE0 and Bound to sampler2D texture0 by default - glBindTexture(GL_TEXTURE_2D, batch->draws[i].textureId); - - if ((batch->draws[i].mode == RL_LINES) || (batch->draws[i].mode == RL_TRIANGLES)) glDrawArrays(batch->draws[i].mode, vertexOffset, batch->draws[i].vertexCount); - else - { -#if defined(GRAPHICS_API_OPENGL_33) - // We need to define the number of indices to be processed: elementCount*6 - // NOTE: The final parameter tells the GPU the offset in bytes from the - // start of the index buffer to the location of the first index to process - glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(vertexOffset/4*6*sizeof(GLuint))); -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) - glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(vertexOffset/4*6*sizeof(GLushort))); -#endif - } - - vertexOffset += (batch->draws[i].vertexCount + batch->draws[i].vertexAlignment); - } - - if (!RLGL.ExtSupported.vao) - { - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - - glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures - } - - if (RLGL.ExtSupported.vao) glBindVertexArray(0); // Unbind VAO - - glUseProgram(0); // Unbind shader program - } - - // Restore viewport to default measures - if (eyeCount == 2) rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); - //------------------------------------------------------------------------------------------------------------ - - // Reset batch buffers - //------------------------------------------------------------------------------------------------------------ - // Reset vertex counter for next frame - RLGL.State.vertexCounter = 0; - - // Reset depth for next draw - batch->currentDepth = -1.0f; - - // Restore projection/modelview matrices - RLGL.State.projection = matProjection; - RLGL.State.modelview = matModelView; - - // Reset RLGL.currentBatch->draws array - for (int i = 0; i < RL_DEFAULT_BATCH_DRAWCALLS; i++) - { - batch->draws[i].mode = RL_QUADS; - batch->draws[i].vertexCount = 0; - batch->draws[i].textureId = RLGL.State.defaultTextureId; - } - - // Reset active texture units for next batch - for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) RLGL.State.activeTextureId[i] = 0; - - // Reset draws counter to one draw for the batch - batch->drawCounter = 1; - //------------------------------------------------------------------------------------------------------------ - - // Change to next buffer in the list (in case of multi-buffering) - batch->currentBuffer++; - if (batch->currentBuffer >= batch->bufferCount) batch->currentBuffer = 0; -#endif -} - -// Set the active render batch for rlgl -void rlSetRenderBatchActive(rlRenderBatch *batch) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - rlDrawRenderBatch(RLGL.currentBatch); - - if (batch != NULL) RLGL.currentBatch = batch; - else RLGL.currentBatch = &RLGL.defaultBatch; -#endif -} - -// Update and draw internal render batch -void rlDrawRenderBatchActive(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside -#endif -} - -// Check internal buffer overflow for a given number of vertex -// and force a rlRenderBatch draw call if required -bool rlCheckRenderBatchLimit(int vCount) -{ - bool overflow = false; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((RLGL.State.vertexCounter + vCount) >= - (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4)) - { - overflow = true; - - // Store current primitive drawing mode and texture id - int currentMode = RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode; - int currentTexture = RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId; - - rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside - - // Restore state of last batch so we can continue adding vertices - RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = currentMode; - RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = currentTexture; - } -#endif - - return overflow; -} - -// Textures data management -//----------------------------------------------------------------------------------------- -// Convert image data to OpenGL texture (returns OpenGL valid Id) -unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount) -{ - unsigned int id = 0; - - glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding - - // Check texture format support by OpenGL 1.1 (compressed textures not supported) -#if defined(GRAPHICS_API_OPENGL_11) - if (format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) - { - TRACELOG(RL_LOG_WARNING, "GL: OpenGL 1.1 does not support GPU compressed texture formats"); - return id; - } -#else - if ((!RLGL.ExtSupported.texCompDXT) && ((format == RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) || (format == RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA) || - (format == RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA) || (format == RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA))) - { - TRACELOG(RL_LOG_WARNING, "GL: DXT compressed texture format not supported"); - return id; - } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((!RLGL.ExtSupported.texCompETC1) && (format == RL_PIXELFORMAT_COMPRESSED_ETC1_RGB)) - { - TRACELOG(RL_LOG_WARNING, "GL: ETC1 compressed texture format not supported"); - return id; - } - - if ((!RLGL.ExtSupported.texCompETC2) && ((format == RL_PIXELFORMAT_COMPRESSED_ETC2_RGB) || (format == RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA))) - { - TRACELOG(RL_LOG_WARNING, "GL: ETC2 compressed texture format not supported"); - return id; - } - - if ((!RLGL.ExtSupported.texCompPVRT) && ((format == RL_PIXELFORMAT_COMPRESSED_PVRT_RGB) || (format == RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA))) - { - TRACELOG(RL_LOG_WARNING, "GL: PVRT compressed texture format not supported"); - return id; - } - - if ((!RLGL.ExtSupported.texCompASTC) && ((format == RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA) || (format == RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA))) - { - TRACELOG(RL_LOG_WARNING, "GL: ASTC compressed texture format not supported"); - return id; - } -#endif -#endif // GRAPHICS_API_OPENGL_11 - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glGenTextures(1, &id); // Generate texture id - - glBindTexture(GL_TEXTURE_2D, id); - - int mipWidth = width; - int mipHeight = height; - int mipOffset = 0; // Mipmap data offset - - // Load the different mipmap levels - for (int i = 0; i < mipmapCount; i++) - { - unsigned int mipSize = rlGetPixelDataSize(mipWidth, mipHeight, format); - - unsigned int glInternalFormat, glFormat, glType; - rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - - TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); - - if (glInternalFormat != -1) - { - if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, (unsigned char *)data + mipOffset); -#if !defined(GRAPHICS_API_OPENGL_11) - else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, (unsigned char *)data + mipOffset); -#endif - -#if defined(GRAPHICS_API_OPENGL_33) - if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) - { - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - } - else if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) - { -#if defined(GRAPHICS_API_OPENGL_21) - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; -#elif defined(GRAPHICS_API_OPENGL_33) - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; -#endif - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - } -#endif - } - - mipWidth /= 2; - mipHeight /= 2; - mipOffset += mipSize; - - // Security check for NPOT textures - if (mipWidth < 1) mipWidth = 1; - if (mipHeight < 1) mipHeight = 1; - } - - // Texture parameters configuration - // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used -#if defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used - if (RLGL.ExtSupported.texNPOT) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis - } - else - { - // NOTE: If using negative texture coordinates (LoadOBJ()), it does not work! - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis - } -#else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis -#endif - - // Magnification and minification filters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Alternative: GL_LINEAR - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Alternative: GL_LINEAR - -#if defined(GRAPHICS_API_OPENGL_33) - if (mipmapCount > 1) - { - // Activate Trilinear filtering if mipmaps are available - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } -#endif - - // At this point we have the texture loaded in GPU and texture parameters configured - - // NOTE: If mipmaps were not in data, they are not generated automatically - - // Unbind current texture - glBindTexture(GL_TEXTURE_2D, 0); - - if (id > 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Texture loaded successfully (%ix%i | %s | %i mipmaps)", id, width, height, rlGetPixelFormatName(format), mipmapCount); - else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load texture"); - - return id; -} - -// Load depth texture/renderbuffer (to be attached to fbo) -// WARNING: OpenGL ES 2.0 requires GL_OES_depth_texture and WebGL requires WEBGL_depth_texture extensions -unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) -{ - unsigned int id = 0; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // In case depth textures not supported, we force renderbuffer usage - if (!RLGL.ExtSupported.texDepth) useRenderBuffer = true; - - // NOTE: We let the implementation to choose the best bit-depth - // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F - unsigned int glInternalFormat = GL_DEPTH_COMPONENT; - -#if defined(GRAPHICS_API_OPENGL_ES2) - // WARNING: WebGL platform requires unsized internal format definition (GL_DEPTH_COMPONENT) - // while other platforms using OpenGL ES 2.0 require/support sized internal formats depending on the GPU capabilities - if (!RLGL.ExtSupported.texDepthWebGL || useRenderBuffer) - { - if (RLGL.ExtSupported.maxDepthBits == 32) glInternalFormat = GL_DEPTH_COMPONENT32_OES; - else if (RLGL.ExtSupported.maxDepthBits == 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES; - else glInternalFormat = GL_DEPTH_COMPONENT16; - } -#endif - - if (!useRenderBuffer && RLGL.ExtSupported.texDepth) - { - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_2D, id); - glTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glBindTexture(GL_TEXTURE_2D, 0); - - TRACELOG(RL_LOG_INFO, "TEXTURE: Depth texture loaded successfully"); - } - else - { - // Create the renderbuffer that will serve as the depth attachment for the framebuffer - // NOTE: A renderbuffer is simpler than a texture and could offer better performance on embedded devices - glGenRenderbuffers(1, &id); - glBindRenderbuffer(GL_RENDERBUFFER, id); - glRenderbufferStorage(GL_RENDERBUFFER, glInternalFormat, width, height); - - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Depth renderbuffer loaded successfully (%i bits)", id, (RLGL.ExtSupported.maxDepthBits >= 24)? RLGL.ExtSupported.maxDepthBits : 16); - } -#endif - - return id; -} - -// Load texture cubemap -// NOTE: Cubemap data is expected to be 6 images in a single data array (one after the other), -// expected the following convention: +X, -X, +Y, -Y, +Z, -Z -unsigned int rlLoadTextureCubemap(const void *data, int size, int format) -{ - unsigned int id = 0; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - unsigned int dataSize = rlGetPixelDataSize(size, size, format); - - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_CUBE_MAP, id); - - unsigned int glInternalFormat, glFormat, glType; - rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - - if (glInternalFormat != -1) - { - // Load cubemap faces - for (unsigned int i = 0; i < 6; i++) - { - if (data == NULL) - { - if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) - { - if (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32) - { - // Instead of using a sized internal texture format (GL_RGB16F, GL_RGB32F), we let the driver to choose the better format for us (GL_RGB) - if (RLGL.ExtSupported.texFloat32) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); - else TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); - } - else if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); - else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, NULL); - } - else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format"); - } - else - { - if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize); - else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, dataSize, (unsigned char *)data + i*dataSize); - } - -#if defined(GRAPHICS_API_OPENGL_33) - if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) - { - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; - glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - } - else if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) - { -#if defined(GRAPHICS_API_OPENGL_21) - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; -#elif defined(GRAPHICS_API_OPENGL_33) - GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; -#endif - glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - } -#endif - } - } - - // Set cubemap texture sampling parameters - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -#if defined(GRAPHICS_API_OPENGL_33) - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Flag not supported on OpenGL ES 2.0 -#endif - - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); -#endif - - if (id > 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Cubemap texture loaded successfully (%ix%i)", id, size, size); - else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load cubemap texture"); - - return id; -} - -// Update already loaded texture in GPU with new data -// NOTE: We don't know safely if internal texture format is the expected one... -void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data) -{ - glBindTexture(GL_TEXTURE_2D, id); - - unsigned int glInternalFormat, glFormat, glType; - rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - - if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) - { - glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, data); - } - else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to update for current texture format (%i)", id, format); -} - -// Get OpenGL internal formats and data type from raylib PixelFormat -void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType) -{ - *glInternalFormat = 0; - *glFormat = 0; - *glType = 0; - - switch (format) - { - #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA - case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; - case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; - #if !defined(GRAPHICS_API_OPENGL_11) - case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - #endif - #elif defined(GRAPHICS_API_OPENGL_33) - case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; - case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; - #endif - #if !defined(GRAPHICS_API_OPENGL_11) - case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; - case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; - case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; - case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; - case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: if (RLGL.ExtSupported.texCompETC1) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU - case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU - case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - #endif - default: TRACELOG(RL_LOG_WARNING, "TEXTURE: Current format not supported (%i)", format); break; - } -} - -// Unload texture from GPU memory -void rlUnloadTexture(unsigned int id) -{ - glDeleteTextures(1, &id); -} - -// Generate mipmap data for selected texture -// NOTE: Only supports GPU mipmap generation -void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindTexture(GL_TEXTURE_2D, id); - - // Check if texture is power-of-two (POT) - bool texIsPOT = false; - - if (((width > 0) && ((width & (width - 1)) == 0)) && - ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; - - if ((texIsPOT) || (RLGL.ExtSupported.texNPOT)) - { - //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorithm: GL_FASTEST, GL_NICEST, GL_DONT_CARE - glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically - - #define MIN(a,b) (((a)<(b))? (a):(b)) - #define MAX(a,b) (((a)>(b))? (a):(b)) - - *mipmaps = 1 + (int)floor(log(MAX(width, height))/log(2)); - TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Mipmaps generated automatically, total: %i", id, *mipmaps); - } - else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps", id); - - glBindTexture(GL_TEXTURE_2D, 0); -#else - TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] GPU mipmap generation not supported", id); -#endif -} - - -// Read texture pixel data -void *rlReadTexturePixels(unsigned int id, int width, int height, int format) -{ - void *pixels = NULL; - -#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) - glBindTexture(GL_TEXTURE_2D, id); - - // NOTE: Using texture id, we can retrieve some texture info (but not on OpenGL ES 2.0) - // Possible texture info: GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE - //int width, height, format; - //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); - //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); - //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); - - // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding. - // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting. - // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) - // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - unsigned int glInternalFormat, glFormat, glType; - rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - unsigned int size = rlGetPixelDataSize(width, height, format); - - if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) - { - pixels = RL_MALLOC(size); - glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); - } - else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Data retrieval not suported for pixel format (%i)", id, format); - - glBindTexture(GL_TEXTURE_2D, 0); -#endif - -#if defined(GRAPHICS_API_OPENGL_ES2) - // glGetTexImage() is not available on OpenGL ES 2.0 - // Texture width and height are required on OpenGL ES 2.0. There is no way to get it from texture id. - // Two possible Options: - // 1 - Bind texture to color fbo attachment and glReadPixels() - // 2 - Create an fbo, activate it, render quad with texture, glReadPixels() - // We are using Option 1, just need to care for texture format on retrieval - // NOTE: This behaviour could be conditioned by graphic driver... - unsigned int fboId = rlLoadFramebuffer(width, height); - - glBindFramebuffer(GL_FRAMEBUFFER, fboId); - glBindTexture(GL_TEXTURE_2D, 0); - - // Attach our texture to FBO - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0); - - // We read data as RGBA because FBO texture is configured as RGBA, despite binding another texture format - pixels = (unsigned char *)RL_MALLOC(rlGetPixelDataSize(width, height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)); - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - // Clean up temporal fbo - rlUnloadFramebuffer(fboId); -#endif - - return pixels; -} - -// Read screen pixel data (color buffer) -unsigned char *rlReadScreenPixels(int width, int height) -{ - unsigned char *screenData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char)); - - // NOTE 1: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer - // NOTE 2: We are getting alpha channel! Be careful, it can be transparent if not cleared properly! - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData); - - // Flip image vertically! - unsigned char *imgData = (unsigned char *)RL_MALLOC(width*height*4*sizeof(unsigned char)); - - for (int y = height - 1; y >= 0; y--) - { - for (int x = 0; x < (width*4); x++) - { - imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; // Flip line - - // Set alpha component value to 255 (no trasparent image retrieval) - // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it! - if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255; - } - } - - RL_FREE(screenData); - - return imgData; // NOTE: image data should be freed -} - -// Framebuffer management (fbo) -//----------------------------------------------------------------------------------------- -// Load a framebuffer to be used for rendering -// NOTE: No textures attached -unsigned int rlLoadFramebuffer(int width, int height) -{ - unsigned int fboId = 0; - -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) - glGenFramebuffers(1, &fboId); // Create the framebuffer object - glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer -#endif - - return fboId; -} - -// Attach color buffer texture to an fbo (unloads previous attachment) -// NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture -void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) - glBindFramebuffer(GL_FRAMEBUFFER, fboId); - - switch (attachType) - { - case RL_ATTACHMENT_COLOR_CHANNEL0: - case RL_ATTACHMENT_COLOR_CHANNEL1: - case RL_ATTACHMENT_COLOR_CHANNEL2: - case RL_ATTACHMENT_COLOR_CHANNEL3: - case RL_ATTACHMENT_COLOR_CHANNEL4: - case RL_ATTACHMENT_COLOR_CHANNEL5: - case RL_ATTACHMENT_COLOR_CHANNEL6: - case RL_ATTACHMENT_COLOR_CHANNEL7: - { - if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, texId, mipLevel); - else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_RENDERBUFFER, texId); - else if (texType >= RL_ATTACHMENT_CUBEMAP_POSITIVE_X) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_CUBE_MAP_POSITIVE_X + texType, texId, mipLevel); - - } break; - case RL_ATTACHMENT_DEPTH: - { - if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); - else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, texId); - - } break; - case RL_ATTACHMENT_STENCIL: - { - if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); - else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, texId); - - } break; - default: break; - } - - glBindFramebuffer(GL_FRAMEBUFFER, 0); -#endif -} - -// Verify render texture is complete -bool rlFramebufferComplete(unsigned int id) -{ - bool result = false; - -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) - glBindFramebuffer(GL_FRAMEBUFFER, id); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) - { - switch (status) - { - case GL_FRAMEBUFFER_UNSUPPORTED: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer is unsupported", id); break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete attachment", id); break; -#if defined(GRAPHICS_API_OPENGL_ES2) - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete dimensions", id); break; -#endif - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer has a missing attachment", id); break; - default: break; - } - } - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - result = (status == GL_FRAMEBUFFER_COMPLETE); -#endif - - return result; -} - -// Unload framebuffer from GPU memory -// NOTE: All attached textures/cubemaps/renderbuffers are also deleted -void rlUnloadFramebuffer(unsigned int id) -{ -#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) - - // Query depth attachment to automatically delete texture/renderbuffer - int depthType = 0, depthId = 0; - glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType); - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId); - - unsigned int depthIdU = (unsigned int)depthId; - if (depthType == GL_RENDERBUFFER) glDeleteRenderbuffers(1, &depthIdU); - else if (depthType == GL_TEXTURE) glDeleteTextures(1, &depthIdU); - - // NOTE: If a texture object is deleted while its image is attached to the *currently bound* framebuffer, - // the texture image is automatically detached from the currently bound framebuffer. - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &id); - - TRACELOG(RL_LOG_INFO, "FBO: [ID %i] Unloaded framebuffer from VRAM (GPU)", id); -#endif -} - -// Vertex data management -//----------------------------------------------------------------------------------------- -// Load a new attributes buffer -unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic) -{ - unsigned int id = 0; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glGenBuffers(1, &id); - glBindBuffer(GL_ARRAY_BUFFER, id); - glBufferData(GL_ARRAY_BUFFER, size, buffer, dynamic? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); -#endif - - return id; -} - -// Load a new attributes element buffer -unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic) -{ - unsigned int id = 0; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glGenBuffers(1, &id); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, buffer, dynamic? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); -#endif - - return id; -} - -// Enable vertex buffer (VBO) -void rlEnableVertexBuffer(unsigned int id) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindBuffer(GL_ARRAY_BUFFER, id); -#endif -} - -// Disable vertex buffer (VBO) -void rlDisableVertexBuffer(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindBuffer(GL_ARRAY_BUFFER, 0); -#endif -} - -// Enable vertex buffer element (VBO element) -void rlEnableVertexBufferElement(unsigned int id) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); -#endif -} - -// Disable vertex buffer element (VBO element) -void rlDisableVertexBufferElement(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -#endif -} - -// Update vertex buffer with new data -// NOTE: dataSize and offset must be provided in bytes -void rlUpdateVertexBuffer(unsigned int id, const void *data, int dataSize, int offset) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindBuffer(GL_ARRAY_BUFFER, id); - glBufferSubData(GL_ARRAY_BUFFER, offset, dataSize, data); -#endif -} - -// Update vertex buffer elements with new data -// NOTE: dataSize and offset must be provided in bytes -void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, dataSize, data); -#endif -} - -// Enable vertex array object (VAO) -bool rlEnableVertexArray(unsigned int vaoId) -{ - bool result = false; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.ExtSupported.vao) - { - glBindVertexArray(vaoId); - result = true; - } -#endif - return result; -} - -// Disable vertex array object (VAO) -void rlDisableVertexArray(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.ExtSupported.vao) glBindVertexArray(0); -#endif -} - -// Enable vertex attribute index -void rlEnableVertexAttribute(unsigned int index) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glEnableVertexAttribArray(index); -#endif -} - -// Disable vertex attribute index -void rlDisableVertexAttribute(unsigned int index) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDisableVertexAttribArray(index); -#endif -} - -// Draw vertex array -void rlDrawVertexArray(int offset, int count) -{ - glDrawArrays(GL_TRIANGLES, offset, count); -} - -// Draw vertex array elements -void rlDrawVertexArrayElements(int offset, int count, const void *buffer) -{ - glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset); -} - -// Draw vertex array instanced -void rlDrawVertexArrayInstanced(int offset, int count, int instances) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDrawArraysInstanced(GL_TRIANGLES, 0, count, instances); -#endif -} - -// Draw vertex array elements instanced -void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset, instances); -#endif -} - -#if defined(GRAPHICS_API_OPENGL_11) -// Enable vertex state pointer -void rlEnableStatePointer(int vertexAttribType, void *buffer) -{ - if (buffer != NULL) glEnableClientState(vertexAttribType); - switch (vertexAttribType) - { - case GL_VERTEX_ARRAY: glVertexPointer(3, GL_FLOAT, 0, buffer); break; - case GL_TEXTURE_COORD_ARRAY: glTexCoordPointer(2, GL_FLOAT, 0, buffer); break; - case GL_NORMAL_ARRAY: if (buffer != NULL) glNormalPointer(GL_FLOAT, 0, buffer); break; - case GL_COLOR_ARRAY: if (buffer != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, buffer); break; - //case GL_INDEX_ARRAY: if (buffer != NULL) glIndexPointer(GL_SHORT, 0, buffer); break; // Indexed colors - default: break; - } -} - -// Disable vertex state pointer -void rlDisableStatePointer(int vertexAttribType) -{ - glDisableClientState(vertexAttribType); -} -#endif - -// Load vertex array object (VAO) -unsigned int rlLoadVertexArray(void) -{ - unsigned int vaoId = 0; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.ExtSupported.vao) - { - glGenVertexArrays(1, &vaoId); - } -#endif - return vaoId; -} - -// Set vertex attribute -void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, const void *pointer) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glVertexAttribPointer(index, compSize, type, normalized, stride, pointer); -#endif -} - -// Set vertex attribute divisor -void rlSetVertexAttributeDivisor(unsigned int index, int divisor) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glVertexAttribDivisor(index, divisor); -#endif -} - -// Unload vertex array object (VAO) -void rlUnloadVertexArray(unsigned int vaoId) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.ExtSupported.vao) - { - glBindVertexArray(0); - glDeleteVertexArrays(1, &vaoId); - TRACELOG(RL_LOG_INFO, "VAO: [ID %i] Unloaded vertex array data from VRAM (GPU)", vaoId); - } -#endif -} - -// Unload vertex buffer (VBO) -void rlUnloadVertexBuffer(unsigned int vboId) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDeleteBuffers(1, &vboId); - //TRACELOG(RL_LOG_INFO, "VBO: Unloaded vertex data from VRAM (GPU)"); -#endif -} - -// Shaders management -//----------------------------------------------------------------------------------------------- -// Load shader from code strings -// NOTE: If shader string is NULL, using default vertex/fragment shaders -unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) -{ - unsigned int id = 0; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - unsigned int vertexShaderId = 0; - unsigned int fragmentShaderId = 0; - - // Compile vertex shader (if provided) - if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER); - // In case no vertex shader was provided or compilation failed, we use default vertex shader - if (vertexShaderId == 0) vertexShaderId = RLGL.State.defaultVShaderId; - - // Compile fragment shader (if provided) - if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER); - // In case no fragment shader was provided or compilation failed, we use default fragment shader - if (fragmentShaderId == 0) fragmentShaderId = RLGL.State.defaultFShaderId; - - // In case vertex and fragment shader are the default ones, no need to recompile, we can just assign the default shader program id - if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId; - else - { - // One of or both shader are new, we need to compile a new shader program - id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId); - - // We can detach and delete vertex/fragment shaders (if not default ones) - // NOTE: We detach shader before deletion to make sure memory is freed - if (vertexShaderId != RLGL.State.defaultVShaderId) - { - // WARNING: Shader program linkage could fail and returned id is 0 - if (id > 0) glDetachShader(id, vertexShaderId); - glDeleteShader(vertexShaderId); - } - if (fragmentShaderId != RLGL.State.defaultFShaderId) - { - // WARNING: Shader program linkage could fail and returned id is 0 - if (id > 0) glDetachShader(id, fragmentShaderId); - glDeleteShader(fragmentShaderId); - } - - // In case shader program loading failed, we assign default shader - if (id == 0) - { - // In case shader loading fails, we return the default shader - TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader"); - id = RLGL.State.defaultShaderId; - } - /* - else - { - // Get available shader uniforms - // NOTE: This information is useful for debug... - int uniformCount = -1; - glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); - - for (int i = 0; i < uniformCount; i++) - { - int namelen = -1; - int num = -1; - char name[256] = { 0 }; // Assume no variable names longer than 256 - GLenum type = GL_ZERO; - - // Get the name of the uniforms - glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name); - - name[namelen] = 0; - TRACELOGD("SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); - } - } - */ - } -#endif - - return id; -} - -// Compile custom shader and return shader id -unsigned int rlCompileShader(const char *shaderCode, int type) -{ - unsigned int shader = 0; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - shader = glCreateShader(type); - glShaderSource(shader, 1, &shaderCode, NULL); - - GLint success = 0; - glCompileShader(shader); - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - - if (success == GL_FALSE) - { - switch (type) - { - case GL_VERTEX_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile vertex shader code", shader); break; - case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile fragment shader code", shader); break; - //case GL_GEOMETRY_SHADER: - #if defined(GRAPHICS_API_OPENGL_43) - case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile compute shader code", shader); break; - #endif - default: break; - } - - int maxLength = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); - - if (maxLength > 0) - { - int length = 0; - char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); - glGetShaderInfoLog(shader, maxLength, &length, log); - TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shader, log); - RL_FREE(log); - } - } - else - { - switch (type) - { - case GL_VERTEX_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Vertex shader compiled successfully", shader); break; - case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Fragment shader compiled successfully", shader); break; - //case GL_GEOMETRY_SHADER: - #if defined(GRAPHICS_API_OPENGL_43) - case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader compiled successfully", shader); break; - #endif - default: break; - } - } -#endif - - return shader; -} - -// Load custom shader strings and return program id -unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) -{ - unsigned int program = 0; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - GLint success = 0; - program = glCreateProgram(); - - glAttachShader(program, vShaderId); - glAttachShader(program, fShaderId); - - // NOTE: Default attribute shader locations must be Bound before linking - glBindAttribLocation(program, 0, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION); - glBindAttribLocation(program, 1, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); - glBindAttribLocation(program, 2, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL); - glBindAttribLocation(program, 3, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR); - glBindAttribLocation(program, 4, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT); - glBindAttribLocation(program, 5, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2); - - // NOTE: If some attrib name is no found on the shader, it locations becomes -1 - - glLinkProgram(program); - - // NOTE: All uniform variables are intitialised to 0 when a program links - - glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (success == GL_FALSE) - { - TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link shader program", program); - - int maxLength = 0; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); - - if (maxLength > 0) - { - int length = 0; - char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); - glGetProgramInfoLog(program, maxLength, &length, log); - TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log); - RL_FREE(log); - } - - glDeleteProgram(program); - - program = 0; - } - else - { - // Get the size of compiled shader program (not available on OpenGL ES 2.0) - // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero. - //GLint binarySize = 0; - //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize); - - TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Program shader loaded successfully", program); - } -#endif - return program; -} - -// Unload shader program -void rlUnloadShaderProgram(unsigned int id) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDeleteProgram(id); - - TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Unloaded shader program data from VRAM (GPU)", id); -#endif -} - -// Get shader location uniform -int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) -{ - int location = -1; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - location = glGetUniformLocation(shaderId, uniformName); - - if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName); - else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location); -#endif - return location; -} - -// Get shader location attribute -int rlGetLocationAttrib(unsigned int shaderId, const char *attribName) -{ - int location = -1; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - location = glGetAttribLocation(shaderId, attribName); - - if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName); - else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location); -#endif - return location; -} - -// Set shader value uniform -void rlSetUniform(int locIndex, const void *value, int uniformType, int count) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - switch (uniformType) - { - case RL_SHADER_UNIFORM_FLOAT: glUniform1fv(locIndex, count, (float *)value); break; - case RL_SHADER_UNIFORM_VEC2: glUniform2fv(locIndex, count, (float *)value); break; - case RL_SHADER_UNIFORM_VEC3: glUniform3fv(locIndex, count, (float *)value); break; - case RL_SHADER_UNIFORM_VEC4: glUniform4fv(locIndex, count, (float *)value); break; - case RL_SHADER_UNIFORM_INT: glUniform1iv(locIndex, count, (int *)value); break; - case RL_SHADER_UNIFORM_IVEC2: glUniform2iv(locIndex, count, (int *)value); break; - case RL_SHADER_UNIFORM_IVEC3: glUniform3iv(locIndex, count, (int *)value); break; - case RL_SHADER_UNIFORM_IVEC4: glUniform4iv(locIndex, count, (int *)value); break; - case RL_SHADER_UNIFORM_SAMPLER2D: glUniform1iv(locIndex, count, (int *)value); break; - default: TRACELOG(RL_LOG_WARNING, "SHADER: Failed to set uniform value, data type not recognized"); - } -#endif -} - -// Set shader value attribute -void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - switch (attribType) - { - case RL_SHADER_ATTRIB_FLOAT: if (count == 1) glVertexAttrib1fv(locIndex, (float *)value); break; - case RL_SHADER_ATTRIB_VEC2: if (count == 2) glVertexAttrib2fv(locIndex, (float *)value); break; - case RL_SHADER_ATTRIB_VEC3: if (count == 3) glVertexAttrib3fv(locIndex, (float *)value); break; - case RL_SHADER_ATTRIB_VEC4: if (count == 4) glVertexAttrib4fv(locIndex, (float *)value); break; - default: TRACELOG(RL_LOG_WARNING, "SHADER: Failed to set attrib default value, data type not recognized"); - } -#endif -} - -// Set shader value uniform matrix -void rlSetUniformMatrix(int locIndex, Matrix mat) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - float matfloat[16] = { - mat.m0, mat.m1, mat.m2, mat.m3, - mat.m4, mat.m5, mat.m6, mat.m7, - mat.m8, mat.m9, mat.m10, mat.m11, - mat.m12, mat.m13, mat.m14, mat.m15 - }; - glUniformMatrix4fv(locIndex, 1, false, matfloat); -#endif -} - -// Set shader value uniform sampler -void rlSetUniformSampler(int locIndex, unsigned int textureId) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // Check if texture is already active - for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) if (RLGL.State.activeTextureId[i] == textureId) return; - - // Register a new active texture for the internal batch system - // NOTE: Default texture is always activated as GL_TEXTURE0 - for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) - { - if (RLGL.State.activeTextureId[i] == 0) - { - glUniform1i(locIndex, 1 + i); // Activate new texture unit - RLGL.State.activeTextureId[i] = textureId; // Save texture id for binding on drawing - break; - } - } -#endif -} - -// Set shader currently active (id and locations) -void rlSetShader(unsigned int id, int *locs) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (RLGL.State.currentShaderId != id) - { - rlDrawRenderBatch(RLGL.currentBatch); - RLGL.State.currentShaderId = id; - RLGL.State.currentShaderLocs = locs; - } -#endif -} - -// Load compute shader program -unsigned int rlLoadComputeShaderProgram(unsigned int shaderId) -{ - unsigned int program = 0; - -#if defined(GRAPHICS_API_OPENGL_43) - GLint success = 0; - program = glCreateProgram(); - glAttachShader(program, shaderId); - glLinkProgram(program); - - // NOTE: All uniform variables are intitialised to 0 when a program links - - glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (success == GL_FALSE) - { - TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link compute shader program", program); - - int maxLength = 0; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); - - if (maxLength > 0) - { - int length = 0; - char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); - glGetProgramInfoLog(program, maxLength, &length, log); - TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log); - RL_FREE(log); - } - - glDeleteProgram(program); - - program = 0; - } - else - { - // Get the size of compiled shader program (not available on OpenGL ES 2.0) - // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero. - //GLint binarySize = 0; - //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize); - - TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", program); - } -#endif - - return program; -} - -// Dispatch compute shader (equivalent to *draw* for graphics pilepine) -void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ) -{ -#if defined(GRAPHICS_API_OPENGL_43) - glDispatchCompute(groupX, groupY, groupZ); -#endif -} - -// Load shader storage buffer object (SSBO) -unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHint) -{ - unsigned int ssbo = 0; - -#if defined(GRAPHICS_API_OPENGL_43) - glGenBuffers(1, &ssbo); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); - glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY); - glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, 0); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); -#endif - - return ssbo; -} - -// Unload shader storage buffer object (SSBO) -void rlUnloadShaderBuffer(unsigned int ssboId) -{ -#if defined(GRAPHICS_API_OPENGL_43) - glDeleteBuffers(1, &ssboId); -#endif -} - -// Update SSBO buffer data -void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSize, unsigned int offset) -{ -#if defined(GRAPHICS_API_OPENGL_43) - glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); - glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, dataSize, data); -#endif -} - -// Get SSBO buffer size -unsigned int rlGetShaderBufferSize(unsigned int id) -{ - long long size = 0; - -#if defined(GRAPHICS_API_OPENGL_43) - glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); - glGetInteger64v(GL_SHADER_STORAGE_BUFFER_SIZE, &size); -#endif - - return (size > 0)? (unsigned int)size : 0; -} - -// Read SSBO buffer data (GPU->CPU) -void rlReadShaderBuffer(unsigned int id, void *dest, unsigned int count, unsigned int offset) -{ -#if defined(GRAPHICS_API_OPENGL_43) - glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); - glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, count, dest); -#endif -} - -// Bind SSBO buffer -void rlBindShaderBuffer(unsigned int id, unsigned int index) -{ -#if defined(GRAPHICS_API_OPENGL_43) - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, index, id); -#endif -} - -// Copy SSBO buffer data -void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned int destOffset, unsigned int srcOffset, unsigned int count) -{ -#if defined(GRAPHICS_API_OPENGL_43) - glBindBuffer(GL_COPY_READ_BUFFER, srcId); - glBindBuffer(GL_COPY_WRITE_BUFFER, destId); - glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, srcOffset, destOffset, count); -#endif -} - -// Bind image texture -void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool readonly) -{ -#if defined(GRAPHICS_API_OPENGL_43) - unsigned int glInternalFormat = 0, glFormat = 0, glType = 0; - - rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - glBindImageTexture(index, id, 0, 0, 0, readonly ? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat); -#endif -} - -// Matrix state management -//----------------------------------------------------------------------------------------- -// Get internal modelview matrix -Matrix rlGetMatrixModelview(void) -{ - Matrix matrix = rlMatrixIdentity(); -#if defined(GRAPHICS_API_OPENGL_11) - float mat[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, mat); - matrix.m0 = mat[0]; - matrix.m1 = mat[1]; - matrix.m2 = mat[2]; - matrix.m3 = mat[3]; - matrix.m4 = mat[4]; - matrix.m5 = mat[5]; - matrix.m6 = mat[6]; - matrix.m7 = mat[7]; - matrix.m8 = mat[8]; - matrix.m9 = mat[9]; - matrix.m10 = mat[10]; - matrix.m11 = mat[11]; - matrix.m12 = mat[12]; - matrix.m13 = mat[13]; - matrix.m14 = mat[14]; - matrix.m15 = mat[15]; -#else - matrix = RLGL.State.modelview; -#endif - return matrix; -} - -// Get internal projection matrix -Matrix rlGetMatrixProjection(void) -{ -#if defined(GRAPHICS_API_OPENGL_11) - float mat[16]; - glGetFloatv(GL_PROJECTION_MATRIX,mat); - Matrix m; - m.m0 = mat[0]; - m.m1 = mat[1]; - m.m2 = mat[2]; - m.m3 = mat[3]; - m.m4 = mat[4]; - m.m5 = mat[5]; - m.m6 = mat[6]; - m.m7 = mat[7]; - m.m8 = mat[8]; - m.m9 = mat[9]; - m.m10 = mat[10]; - m.m11 = mat[11]; - m.m12 = mat[12]; - m.m13 = mat[13]; - m.m14 = mat[14]; - m.m15 = mat[15]; - return m; -#else - return RLGL.State.projection; -#endif -} - -// Get internal accumulated transform matrix -Matrix rlGetMatrixTransform(void) -{ - Matrix mat = rlMatrixIdentity(); -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - // TODO: Consider possible transform matrices in the RLGL.State.stack - // Is this the right order? or should we start with the first stored matrix instead of the last one? - //Matrix matStackTransform = rlMatrixIdentity(); - //for (int i = RLGL.State.stackCounter; i > 0; i--) matStackTransform = rlMatrixMultiply(RLGL.State.stack[i], matStackTransform); - mat = RLGL.State.transform; -#endif - return mat; -} - -// Get internal projection matrix for stereo render (selected eye) -RLAPI Matrix rlGetMatrixProjectionStereo(int eye) -{ - Matrix mat = rlMatrixIdentity(); -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - mat = RLGL.State.projectionStereo[eye]; -#endif - return mat; -} - -// Get internal view offset matrix for stereo render (selected eye) -RLAPI Matrix rlGetMatrixViewOffsetStereo(int eye) -{ - Matrix mat = rlMatrixIdentity(); -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - mat = RLGL.State.viewOffsetStereo[eye]; -#endif - return mat; -} - -// Set a custom modelview matrix (replaces internal modelview matrix) -void rlSetMatrixModelview(Matrix view) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - RLGL.State.modelview = view; -#endif -} - -// Set a custom projection matrix (replaces internal projection matrix) -void rlSetMatrixProjection(Matrix projection) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - RLGL.State.projection = projection; -#endif -} - -// Set eyes projection matrices for stereo rendering -void rlSetMatrixProjectionStereo(Matrix right, Matrix left) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - RLGL.State.projectionStereo[0] = right; - RLGL.State.projectionStereo[1] = left; -#endif -} - -// Set eyes view offsets matrices for stereo rendering -void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - RLGL.State.viewOffsetStereo[0] = right; - RLGL.State.viewOffsetStereo[1] = left; -#endif -} - -// Load and draw a quad in NDC -void rlLoadDrawQuad(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - unsigned int quadVAO = 0; - unsigned int quadVBO = 0; - - float vertices[] = { - // Positions Texcoords - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, - }; - - // Gen VAO to contain VBO - glGenVertexArrays(1, &quadVAO); - glBindVertexArray(quadVAO); - - // Gen and fill vertex buffer (VBO) - glGenBuffers(1, &quadVBO); - glBindBuffer(GL_ARRAY_BUFFER, quadVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW); - - // Bind vertex attributes (position, texcoords) - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0); // Positions - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)(3*sizeof(float))); // Texcoords - - // Draw quad - glBindVertexArray(quadVAO); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glBindVertexArray(0); - - // Delete buffers (VBO and VAO) - glDeleteBuffers(1, &quadVBO); - glDeleteVertexArrays(1, &quadVAO); -#endif -} - -// Load and draw a cube in NDC -void rlLoadDrawCube(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - unsigned int cubeVAO = 0; - unsigned int cubeVBO = 0; - - float vertices[] = { - // Positions Normals Texcoords - -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, - 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, - 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, - 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, - -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, - -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, - 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, - -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, - -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, - -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, - -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, - 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, - -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, - 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, - 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, - -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, - -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, - 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, - -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, - -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f - }; - - // Gen VAO to contain VBO - glGenVertexArrays(1, &cubeVAO); - glBindVertexArray(cubeVAO); - - // Gen and fill vertex buffer (VBO) - glGenBuffers(1, &cubeVBO); - glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - // Bind vertex attributes (position, normals, texcoords) - glBindVertexArray(cubeVAO); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)0); // Positions - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(3*sizeof(float))); // Normals - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(6*sizeof(float))); // Texcoords - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - - // Draw cube - glBindVertexArray(cubeVAO); - glDrawArrays(GL_TRIANGLES, 0, 36); - glBindVertexArray(0); - - // Delete VBO and VAO - glDeleteBuffers(1, &cubeVBO); - glDeleteVertexArrays(1, &cubeVAO); -#endif -} - -// Get name string for pixel format -const char *rlGetPixelFormatName(unsigned int format) -{ - switch (format) - { - case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: return "GRAYSCALE"; break; // 8 bit per pixel (no alpha) - case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: return "GRAY_ALPHA"; break; // 8*2 bpp (2 channels) - case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: return "R5G6B5"; break; // 16 bpp - case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: return "R8G8B8"; break; // 24 bpp - case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: return "R5G5B5A1"; break; // 16 bpp (1 bit alpha) - case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: return "R4G4B4A4"; break; // 16 bpp (4 bit alpha) - case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: return "R8G8B8A8"; break; // 32 bpp - case RL_PIXELFORMAT_UNCOMPRESSED_R32: return "R32"; break; // 32 bpp (1 channel - float) - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: return "R32G32B32"; break; // 32*3 bpp (3 channels - float) - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: return "R32G32B32A32"; break; // 32*4 bpp (4 channels - float) - case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: return "DXT1_RGB"; break; // 4 bpp (no alpha) - case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: return "DXT1_RGBA"; break; // 4 bpp (1 bit alpha) - case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: return "DXT3_RGBA"; break; // 8 bpp - case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: return "DXT5_RGBA"; break; // 8 bpp - case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: return "ETC1_RGB"; break; // 4 bpp - case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB: return "ETC2_RGB"; break; // 4 bpp - case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: return "ETC2_RGBA"; break; // 8 bpp - case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB: return "PVRT_RGB"; break; // 4 bpp - case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: return "PVRT_RGBA"; break; // 4 bpp - case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: return "ASTC_4x4_RGBA"; break; // 8 bpp - case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: return "ASTC_8x8_RGBA"; break; // 2 bpp - default: return "UNKNOWN"; break; - } -} - -//---------------------------------------------------------------------------------- -// Module specific Functions Definition -//---------------------------------------------------------------------------------- -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -// Load default shader (just vertex positioning and texture coloring) -// NOTE: This shader program is used for internal buffers -// NOTE: Loaded: RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs -static void rlLoadShaderDefault(void) -{ - RLGL.State.defaultShaderLocs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); - - // NOTE: All locations must be reseted to -1 (no location) - for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) RLGL.State.defaultShaderLocs[i] = -1; - - // Vertex shader directly defined, no external file required - const char *defaultVShaderCode = -#if defined(GRAPHICS_API_OPENGL_21) - "#version 120 \n" - "attribute vec3 vertexPosition; \n" - "attribute vec2 vertexTexCoord; \n" - "attribute vec4 vertexColor; \n" - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" -#elif defined(GRAPHICS_API_OPENGL_33) - "#version 330 \n" - "in vec3 vertexPosition; \n" - "in vec2 vertexTexCoord; \n" - "in vec4 vertexColor; \n" - "out vec2 fragTexCoord; \n" - "out vec4 fragColor; \n" -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) - "#version 100 \n" - "attribute vec3 vertexPosition; \n" - "attribute vec2 vertexTexCoord; \n" - "attribute vec4 vertexColor; \n" - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" -#endif - "uniform mat4 mvp; \n" - "void main() \n" - "{ \n" - " fragTexCoord = vertexTexCoord; \n" - " fragColor = vertexColor; \n" - " gl_Position = mvp*vec4(vertexPosition, 1.0); \n" - "} \n"; - - // Fragment shader directly defined, no external file required - const char *defaultFShaderCode = -#if defined(GRAPHICS_API_OPENGL_21) - "#version 120 \n" - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" - "uniform sampler2D texture0; \n" - "uniform vec4 colDiffuse; \n" - "void main() \n" - "{ \n" - " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" - " gl_FragColor = texelColor*colDiffuse*fragColor; \n" - "} \n"; -#elif defined(GRAPHICS_API_OPENGL_33) - "#version 330 \n" - "in vec2 fragTexCoord; \n" - "in vec4 fragColor; \n" - "out vec4 finalColor; \n" - "uniform sampler2D texture0; \n" - "uniform vec4 colDiffuse; \n" - "void main() \n" - "{ \n" - " vec4 texelColor = texture(texture0, fragTexCoord); \n" - " finalColor = texelColor*colDiffuse*fragColor; \n" - "} \n"; -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) - "#version 100 \n" - "precision mediump float; \n" // Precision required for OpenGL ES2 (WebGL) - "varying vec2 fragTexCoord; \n" - "varying vec4 fragColor; \n" - "uniform sampler2D texture0; \n" - "uniform vec4 colDiffuse; \n" - "void main() \n" - "{ \n" - " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" - " gl_FragColor = texelColor*colDiffuse*fragColor; \n" - "} \n"; -#endif - - // NOTE: Compiled vertex/fragment shaders are not deleted, - // they are kept for re-use as default shaders in case some shader loading fails - RLGL.State.defaultVShaderId = rlCompileShader(defaultVShaderCode, GL_VERTEX_SHADER); // Compile default vertex shader - RLGL.State.defaultFShaderId = rlCompileShader(defaultFShaderCode, GL_FRAGMENT_SHADER); // Compile default fragment shader - - RLGL.State.defaultShaderId = rlLoadShaderProgram(RLGL.State.defaultVShaderId, RLGL.State.defaultFShaderId); - - if (RLGL.State.defaultShaderId > 0) - { - TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Default shader loaded successfully", RLGL.State.defaultShaderId); - - // Set default shader locations: attributes locations - RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_POSITION] = glGetAttribLocation(RLGL.State.defaultShaderId, "vertexPosition"); - RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(RLGL.State.defaultShaderId, "vertexTexCoord"); - RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_COLOR] = glGetAttribLocation(RLGL.State.defaultShaderId, "vertexColor"); - - // Set default shader locations: uniform locations - RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MATRIX_MVP] = glGetUniformLocation(RLGL.State.defaultShaderId, "mvp"); - RLGL.State.defaultShaderLocs[RL_SHADER_LOC_COLOR_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, "colDiffuse"); - RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MAP_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, "texture0"); - } - else TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to load default shader", RLGL.State.defaultShaderId); -} - -// Unload default shader -// NOTE: Unloads: RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs -static void rlUnloadShaderDefault(void) -{ - glUseProgram(0); - - glDetachShader(RLGL.State.defaultShaderId, RLGL.State.defaultVShaderId); - glDetachShader(RLGL.State.defaultShaderId, RLGL.State.defaultFShaderId); - glDeleteShader(RLGL.State.defaultVShaderId); - glDeleteShader(RLGL.State.defaultFShaderId); - - glDeleteProgram(RLGL.State.defaultShaderId); - - RL_FREE(RLGL.State.defaultShaderLocs); - - TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Default shader unloaded successfully", RLGL.State.defaultShaderId); -} - -#if defined(RLGL_SHOW_GL_DETAILS_INFO) -// Get compressed format official GL identifier name -static char *rlGetCompressedFormatName(int format) -{ - switch (format) - { - // GL_EXT_texture_compression_s3tc - case 0x83F0: return "GL_COMPRESSED_RGB_S3TC_DXT1_EXT"; break; - case 0x83F1: return "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT"; break; - case 0x83F2: return "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT"; break; - case 0x83F3: return "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT"; break; - // GL_3DFX_texture_compression_FXT1 - case 0x86B0: return "GL_COMPRESSED_RGB_FXT1_3DFX"; break; - case 0x86B1: return "GL_COMPRESSED_RGBA_FXT1_3DFX"; break; - // GL_IMG_texture_compression_pvrtc - case 0x8C00: return "GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG"; break; - case 0x8C01: return "GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG"; break; - case 0x8C02: return "GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG"; break; - case 0x8C03: return "GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG"; break; - // GL_OES_compressed_ETC1_RGB8_texture - case 0x8D64: return "GL_ETC1_RGB8_OES"; break; - // GL_ARB_texture_compression_rgtc - case 0x8DBB: return "GL_COMPRESSED_RED_RGTC1"; break; - case 0x8DBC: return "GL_COMPRESSED_SIGNED_RED_RGTC1"; break; - case 0x8DBD: return "GL_COMPRESSED_RG_RGTC2"; break; - case 0x8DBE: return "GL_COMPRESSED_SIGNED_RG_RGTC2"; break; - // GL_ARB_texture_compression_bptc - case 0x8E8C: return "GL_COMPRESSED_RGBA_BPTC_UNORM_ARB"; break; - case 0x8E8D: return "GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB"; break; - case 0x8E8E: return "GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB"; break; - case 0x8E8F: return "GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB"; break; - // GL_ARB_ES3_compatibility - case 0x9274: return "GL_COMPRESSED_RGB8_ETC2"; break; - case 0x9275: return "GL_COMPRESSED_SRGB8_ETC2"; break; - case 0x9276: return "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2"; break; - case 0x9277: return "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2"; break; - case 0x9278: return "GL_COMPRESSED_RGBA8_ETC2_EAC"; break; - case 0x9279: return "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC"; break; - case 0x9270: return "GL_COMPRESSED_R11_EAC"; break; - case 0x9271: return "GL_COMPRESSED_SIGNED_R11_EAC"; break; - case 0x9272: return "GL_COMPRESSED_RG11_EAC"; break; - case 0x9273: return "GL_COMPRESSED_SIGNED_RG11_EAC"; break; - // GL_KHR_texture_compression_astc_hdr - case 0x93B0: return "GL_COMPRESSED_RGBA_ASTC_4x4_KHR"; break; - case 0x93B1: return "GL_COMPRESSED_RGBA_ASTC_5x4_KHR"; break; - case 0x93B2: return "GL_COMPRESSED_RGBA_ASTC_5x5_KHR"; break; - case 0x93B3: return "GL_COMPRESSED_RGBA_ASTC_6x5_KHR"; break; - case 0x93B4: return "GL_COMPRESSED_RGBA_ASTC_6x6_KHR"; break; - case 0x93B5: return "GL_COMPRESSED_RGBA_ASTC_8x5_KHR"; break; - case 0x93B6: return "GL_COMPRESSED_RGBA_ASTC_8x6_KHR"; break; - case 0x93B7: return "GL_COMPRESSED_RGBA_ASTC_8x8_KHR"; break; - case 0x93B8: return "GL_COMPRESSED_RGBA_ASTC_10x5_KHR"; break; - case 0x93B9: return "GL_COMPRESSED_RGBA_ASTC_10x6_KHR"; break; - case 0x93BA: return "GL_COMPRESSED_RGBA_ASTC_10x8_KHR"; break; - case 0x93BB: return "GL_COMPRESSED_RGBA_ASTC_10x10_KHR"; break; - case 0x93BC: return "GL_COMPRESSED_RGBA_ASTC_12x10_KHR"; break; - case 0x93BD: return "GL_COMPRESSED_RGBA_ASTC_12x12_KHR"; break; - case 0x93D0: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR"; break; - case 0x93D1: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR"; break; - case 0x93D2: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR"; break; - case 0x93D3: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR"; break; - case 0x93D4: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR"; break; - case 0x93D5: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR"; break; - case 0x93D6: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR"; break; - case 0x93D7: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR"; break; - case 0x93D8: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR"; break; - case 0x93D9: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR"; break; - case 0x93DA: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR"; break; - case 0x93DB: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR"; break; - case 0x93DC: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR"; break; - case 0x93DD: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR"; break; - default: return "GL_COMPRESSED_UNKNOWN"; break; - } -} -#endif // RLGL_SHOW_GL_DETAILS_INFO - -#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 - -// Get pixel data size in bytes (image or texture) -// NOTE: Size depends on pixel format -static int rlGetPixelDataSize(int width, int height, int format) -{ - int dataSize = 0; // Size in bytes - int bpp = 0; // Bits per pixel - - switch (format) - { - case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; - case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: - case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; - case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: - case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: - case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: - case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB: - case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB: - case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; - case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: - case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: - case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: - case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break; - case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; - default: break; - } - - dataSize = width*height*bpp/8; // Total data size in bytes - - // Most compressed formats works on 4x4 blocks, - // if texture is smaller, minimum dataSize is 8 or 16 - if ((width < 4) && (height < 4)) - { - if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8; - else if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16; - } - - return dataSize; -} - -// Auxiliar math functions - -// Get identity matrix -static Matrix rlMatrixIdentity(void) -{ - Matrix result = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - - return result; -} - -// Get two matrix multiplication -// NOTE: When multiplying matrices... the order matters! -static Matrix rlMatrixMultiply(Matrix left, Matrix right) -{ - Matrix result = { 0 }; - - result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; - result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; - result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; - result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; - result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; - result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; - result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; - result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; - result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; - result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; - result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; - result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; - result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; - result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; - result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; - result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; - - return result; -} - -#endif // RLGL_IMPLEMENTATION +/********************************************************************************************** +* +* rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API +* +* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) +* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) +* +* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are +* initialized on rlglInit() to accumulate vertex data. +* +* When an internal state change is required all the stored vertex data is renderer in batch, +* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. +* +* Some additional resources are also loaded for convenience, here the complete list: +* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data +* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8 +* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs) +* +* Internal buffer (and additional resources) must be manually unloaded calling rlglClose(). +* +* +* CONFIGURATION: +* +* #define GRAPHICS_API_OPENGL_11 +* #define GRAPHICS_API_OPENGL_21 +* #define GRAPHICS_API_OPENGL_33 +* #define GRAPHICS_API_OPENGL_43 +* #define GRAPHICS_API_OPENGL_ES2 +* Use selected OpenGL graphics backend, should be supported by platform +* Those preprocessor defines are only used on rlgl module, if OpenGL version is +* required by any other module, use rlGetVersion() to check it +* +* #define RLGL_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define RLGL_RENDER_TEXTURES_HINT +* Enable framebuffer objects (fbo) support (enabled by default) +* Some GPUs could not support them despite the OpenGL version +* +* #define RLGL_SHOW_GL_DETAILS_INFO +* Show OpenGL extensions and capabilities detailed logs on init +* +* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT +* Enable debug context (only available on OpenGL 4.3) +* +* rlgl capabilities could be customized just defining some internal +* values before library inclusion (default values listed): +* +* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits +* #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) +* #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) +* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +* +* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack +* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported +* #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance +* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance +* +* When loading a shader, the following vertex attribute and uniform +* location names are tried to be set automatically: +* +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) +* +* DEPENDENCIES: +* +* - OpenGL libraries (depending on platform and OpenGL version selected) +* - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RLGL_H +#define RLGL_H + +#define RLGL_VERSION "4.5" + +// Function specifiers in case library is build/used as a shared library (Windows) +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +#if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #endif +#endif + +// Function specifiers definition +#ifndef RLAPI + #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +// Support TRACELOG macros +#ifndef TRACELOG + #define TRACELOG(level, ...) (void)0 + #define TRACELOGD(...) (void)0 +#endif + +// Allow custom memory allocators +#ifndef RL_MALLOC + #define RL_MALLOC(sz) malloc(sz) +#endif +#ifndef RL_CALLOC + #define RL_CALLOC(n,sz) calloc(n,sz) +#endif +#ifndef RL_REALLOC + #define RL_REALLOC(n,sz) realloc(n,sz) +#endif +#ifndef RL_FREE + #define RL_FREE(p) free(p) +#endif + +// Security check in case no GRAPHICS_API_OPENGL_* defined +#if !defined(GRAPHICS_API_OPENGL_11) && \ + !defined(GRAPHICS_API_OPENGL_21) && \ + !defined(GRAPHICS_API_OPENGL_33) && \ + !defined(GRAPHICS_API_OPENGL_43) && \ + !defined(GRAPHICS_API_OPENGL_ES2) + #define GRAPHICS_API_OPENGL_33 +#endif + +// Security check in case multiple GRAPHICS_API_OPENGL_* defined +#if defined(GRAPHICS_API_OPENGL_11) + #if defined(GRAPHICS_API_OPENGL_21) + #undef GRAPHICS_API_OPENGL_21 + #endif + #if defined(GRAPHICS_API_OPENGL_33) + #undef GRAPHICS_API_OPENGL_33 + #endif + #if defined(GRAPHICS_API_OPENGL_43) + #undef GRAPHICS_API_OPENGL_43 + #endif + #if defined(GRAPHICS_API_OPENGL_ES2) + #undef GRAPHICS_API_OPENGL_ES2 + #endif +#endif + +// OpenGL 2.1 uses most of OpenGL 3.3 Core functionality +// WARNING: Specific parts are checked with #if defines +#if defined(GRAPHICS_API_OPENGL_21) + #define GRAPHICS_API_OPENGL_33 +#endif + +// OpenGL 4.3 uses OpenGL 3.3 Core functionality +#if defined(GRAPHICS_API_OPENGL_43) + #define GRAPHICS_API_OPENGL_33 +#endif + +// Support framebuffer objects by default +// NOTE: Some driver implementation do not support it, despite they should +#define RLGL_RENDER_TEXTURES_HINT + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- + +// Default internal render batch elements limits +#ifndef RL_DEFAULT_BATCH_BUFFER_ELEMENTS + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // This is the maximum amount of elements (quads) per batch + // NOTE: Be careful with text, every letter maps to a quad + #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 + #endif + #if defined(GRAPHICS_API_OPENGL_ES2) + // We reduce memory sizes for embedded systems (RPI and HTML5) + // NOTE: On HTML5 (emscripten) this is allocated on heap, + // by default it's only 16MB!...just take care... + #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 2048 + #endif +#endif +#ifndef RL_DEFAULT_BATCH_BUFFERS + #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) +#endif +#ifndef RL_DEFAULT_BATCH_DRAWCALLS + #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) +#endif +#ifndef RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS + #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +#endif + +// Internal Matrix stack +#ifndef RL_MAX_MATRIX_STACK_SIZE + #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of Matrix stack +#endif + +// Shader limits +#ifndef RL_MAX_SHADER_LOCATIONS + #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported +#endif + +// Projection matrix culling +#ifndef RL_CULL_DISTANCE_NEAR + #define RL_CULL_DISTANCE_NEAR 0.01 // Default near cull distance +#endif +#ifndef RL_CULL_DISTANCE_FAR + #define RL_CULL_DISTANCE_FAR 1000.0 // Default far cull distance +#endif + +// Texture parameters (equivalent to OpenGL defines) +#define RL_TEXTURE_WRAP_S 0x2802 // GL_TEXTURE_WRAP_S +#define RL_TEXTURE_WRAP_T 0x2803 // GL_TEXTURE_WRAP_T +#define RL_TEXTURE_MAG_FILTER 0x2800 // GL_TEXTURE_MAG_FILTER +#define RL_TEXTURE_MIN_FILTER 0x2801 // GL_TEXTURE_MIN_FILTER + +#define RL_TEXTURE_FILTER_NEAREST 0x2600 // GL_NEAREST +#define RL_TEXTURE_FILTER_LINEAR 0x2601 // GL_LINEAR +#define RL_TEXTURE_FILTER_MIP_NEAREST 0x2700 // GL_NEAREST_MIPMAP_NEAREST +#define RL_TEXTURE_FILTER_NEAREST_MIP_LINEAR 0x2702 // GL_NEAREST_MIPMAP_LINEAR +#define RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST 0x2701 // GL_LINEAR_MIPMAP_NEAREST +#define RL_TEXTURE_FILTER_MIP_LINEAR 0x2703 // GL_LINEAR_MIPMAP_LINEAR +#define RL_TEXTURE_FILTER_ANISOTROPIC 0x3000 // Anisotropic filter (custom identifier) +#define RL_TEXTURE_MIPMAP_BIAS_RATIO 0x4000 // Texture mipmap bias, percentage ratio (custom identifier) + +#define RL_TEXTURE_WRAP_REPEAT 0x2901 // GL_REPEAT +#define RL_TEXTURE_WRAP_CLAMP 0x812F // GL_CLAMP_TO_EDGE +#define RL_TEXTURE_WRAP_MIRROR_REPEAT 0x8370 // GL_MIRRORED_REPEAT +#define RL_TEXTURE_WRAP_MIRROR_CLAMP 0x8742 // GL_MIRROR_CLAMP_EXT + +// Matrix modes (equivalent to OpenGL) +#define RL_MODELVIEW 0x1700 // GL_MODELVIEW +#define RL_PROJECTION 0x1701 // GL_PROJECTION +#define RL_TEXTURE 0x1702 // GL_TEXTURE + +// Primitive assembly draw modes +#define RL_LINES 0x0001 // GL_LINES +#define RL_TRIANGLES 0x0004 // GL_TRIANGLES +#define RL_QUADS 0x0007 // GL_QUADS + +// GL equivalent data types +#define RL_UNSIGNED_BYTE 0x1401 // GL_UNSIGNED_BYTE +#define RL_FLOAT 0x1406 // GL_FLOAT + +// GL buffer usage hint +#define RL_STREAM_DRAW 0x88E0 // GL_STREAM_DRAW +#define RL_STREAM_READ 0x88E1 // GL_STREAM_READ +#define RL_STREAM_COPY 0x88E2 // GL_STREAM_COPY +#define RL_STATIC_DRAW 0x88E4 // GL_STATIC_DRAW +#define RL_STATIC_READ 0x88E5 // GL_STATIC_READ +#define RL_STATIC_COPY 0x88E6 // GL_STATIC_COPY +#define RL_DYNAMIC_DRAW 0x88E8 // GL_DYNAMIC_DRAW +#define RL_DYNAMIC_READ 0x88E9 // GL_DYNAMIC_READ +#define RL_DYNAMIC_COPY 0x88EA // GL_DYNAMIC_COPY + +// GL Shader type +#define RL_FRAGMENT_SHADER 0x8B30 // GL_FRAGMENT_SHADER +#define RL_VERTEX_SHADER 0x8B31 // GL_VERTEX_SHADER +#define RL_COMPUTE_SHADER 0x91B9 // GL_COMPUTE_SHADER + +// GL blending factors +#define RL_ZERO 0 // GL_ZERO +#define RL_ONE 1 // GL_ONE +#define RL_SRC_COLOR 0x0300 // GL_SRC_COLOR +#define RL_ONE_MINUS_SRC_COLOR 0x0301 // GL_ONE_MINUS_SRC_COLOR +#define RL_SRC_ALPHA 0x0302 // GL_SRC_ALPHA +#define RL_ONE_MINUS_SRC_ALPHA 0x0303 // GL_ONE_MINUS_SRC_ALPHA +#define RL_DST_ALPHA 0x0304 // GL_DST_ALPHA +#define RL_ONE_MINUS_DST_ALPHA 0x0305 // GL_ONE_MINUS_DST_ALPHA +#define RL_DST_COLOR 0x0306 // GL_DST_COLOR +#define RL_ONE_MINUS_DST_COLOR 0x0307 // GL_ONE_MINUS_DST_COLOR +#define RL_SRC_ALPHA_SATURATE 0x0308 // GL_SRC_ALPHA_SATURATE +#define RL_CONSTANT_COLOR 0x8001 // GL_CONSTANT_COLOR +#define RL_ONE_MINUS_CONSTANT_COLOR 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR +#define RL_CONSTANT_ALPHA 0x8003 // GL_CONSTANT_ALPHA +#define RL_ONE_MINUS_CONSTANT_ALPHA 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA + +// GL blending functions/equations +#define RL_FUNC_ADD 0x8006 // GL_FUNC_ADD +#define RL_MIN 0x8007 // GL_MIN +#define RL_MAX 0x8008 // GL_MAX +#define RL_FUNC_SUBTRACT 0x800A // GL_FUNC_SUBTRACT +#define RL_FUNC_REVERSE_SUBTRACT 0x800B // GL_FUNC_REVERSE_SUBTRACT +#define RL_BLEND_EQUATION 0x8009 // GL_BLEND_EQUATION +#define RL_BLEND_EQUATION_RGB 0x8009 // GL_BLEND_EQUATION_RGB // (Same as BLEND_EQUATION) +#define RL_BLEND_EQUATION_ALPHA 0x883D // GL_BLEND_EQUATION_ALPHA +#define RL_BLEND_DST_RGB 0x80C8 // GL_BLEND_DST_RGB +#define RL_BLEND_SRC_RGB 0x80C9 // GL_BLEND_SRC_RGB +#define RL_BLEND_DST_ALPHA 0x80CA // GL_BLEND_DST_ALPHA +#define RL_BLEND_SRC_ALPHA 0x80CB // GL_BLEND_SRC_ALPHA +#define RL_BLEND_COLOR 0x8005 // GL_BLEND_COLOR + + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) + #include +#elif !defined(__cplusplus) && !defined(bool) && !defined(RL_BOOL_TYPE) + // Boolean type +typedef enum bool { false = 0, true = !false } bool; +#endif + +#if !defined(RL_MATRIX_TYPE) +// Matrix, 4x4 components, column major, OpenGL style, right handed +typedef struct Matrix { + float m0, m4, m8, m12; // Matrix first row (4 components) + float m1, m5, m9, m13; // Matrix second row (4 components) + float m2, m6, m10, m14; // Matrix third row (4 components) + float m3, m7, m11, m15; // Matrix fourth row (4 components) +} Matrix; +#define RL_MATRIX_TYPE +#endif + +// Dynamic vertex buffers (position + texcoords + colors + indices arrays) +typedef struct rlVertexBuffer { + int elementCount; // Number of elements in the buffer (QUADS) + + float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + unsigned int *indices; // Vertex indices (in case vertex data comes indexed) (6 indices per quad) +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + unsigned short *indices; // Vertex indices (in case vertex data comes indexed) (6 indices per quad) +#endif + unsigned int vaoId; // OpenGL Vertex Array Object id + unsigned int vboId[4]; // OpenGL Vertex Buffer Objects id (4 types of vertex data) +} rlVertexBuffer; + +// Draw call type +// NOTE: Only texture changes register a new draw, other state-change-related elements are not +// used at this moment (vaoId, shaderId, matrices), raylib just forces a batch draw call if any +// of those state-change happens (this is done in core module) +typedef struct rlDrawCall { + int mode; // Drawing mode: LINES, TRIANGLES, QUADS + int vertexCount; // Number of vertex of the draw + int vertexAlignment; // Number of vertex required for index alignment (LINES, TRIANGLES) + //unsigned int vaoId; // Vertex array id to be used on the draw -> Using RLGL.currentBatch->vertexBuffer.vaoId + //unsigned int shaderId; // Shader id to be used on the draw -> Using RLGL.currentShaderId + unsigned int textureId; // Texture id to be used on the draw -> Use to create new draw call if changes + + //Matrix projection; // Projection matrix for this draw -> Using RLGL.projection by default + //Matrix modelview; // Modelview matrix for this draw -> Using RLGL.modelview by default +} rlDrawCall; + +// rlRenderBatch type +typedef struct rlRenderBatch { + int bufferCount; // Number of vertex buffers (multi-buffering support) + int currentBuffer; // Current buffer tracking in case of multi-buffering + rlVertexBuffer *vertexBuffer; // Dynamic buffer(s) for vertex data + + rlDrawCall *draws; // Draw calls array, depends on textureId + int drawCounter; // Draw calls counter + float currentDepth; // Current depth value for next draw +} rlRenderBatch; + +// OpenGL version +typedef enum { + RL_OPENGL_11 = 1, // OpenGL 1.1 + RL_OPENGL_21, // OpenGL 2.1 (GLSL 120) + RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) + RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330) + RL_OPENGL_ES_20 // OpenGL ES 2.0 (GLSL 100) +} rlGlVersion; + +// Trace log level +// NOTE: Organized by priority level +typedef enum { + RL_LOG_ALL = 0, // Display all logs + RL_LOG_TRACE, // Trace logging, intended for internal use only + RL_LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds + RL_LOG_INFO, // Info logging, used for program execution info + RL_LOG_WARNING, // Warning logging, used on recoverable failures + RL_LOG_ERROR, // Error logging, used on unrecoverable failures + RL_LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) + RL_LOG_NONE // Disable logging +} rlTraceLogLevel; + +// Texture pixel formats +// NOTE: Support depends on OpenGL version +typedef enum { + RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp + RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp + RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp + RL_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) + RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + RL_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) + RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) + RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp + RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp + RL_PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp + RL_PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp + RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + RL_PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp + RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp + RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp +} rlPixelFormat; + +// Texture parameters: filter mode +// NOTE 1: Filtering considers mipmaps if available in the texture +// NOTE 2: Filter is accordingly set for minification and magnification +typedef enum { + RL_TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation + RL_TEXTURE_FILTER_BILINEAR, // Linear filtering + RL_TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) + RL_TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x + RL_TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x + RL_TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x +} rlTextureFilter; + +// Color blending modes (pre-defined) +typedef enum { + RL_BLEND_ALPHA = 0, // Blend textures considering alpha (default) + RL_BLEND_ADDITIVE, // Blend textures adding colors + RL_BLEND_MULTIPLIED, // Blend textures multiplying colors + RL_BLEND_ADD_COLORS, // Blend textures adding colors (alternative) + RL_BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) + RL_BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha + RL_BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + RL_BLEND_CUSTOM_SEPARATE // Blend textures using custom src/dst factors (use rlSetBlendFactorsSeparate()) +} rlBlendMode; + +// Shader location point type +typedef enum { + RL_SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position + RL_SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 + RL_SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 + RL_SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal + RL_SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent + RL_SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color + RL_SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection + RL_SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) + RL_SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection + RL_SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) + RL_SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal + RL_SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view + RL_SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color + RL_SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color + RL_SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color + RL_SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: RL_SHADER_LOC_MAP_DIFFUSE) + RL_SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: RL_SHADER_LOC_MAP_SPECULAR) + RL_SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal + RL_SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness + RL_SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion + RL_SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission + RL_SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height + RL_SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap + RL_SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance + RL_SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter + RL_SHADER_LOC_MAP_BRDF // Shader location: sampler2d texture: brdf +} rlShaderLocationIndex; + +#define RL_SHADER_LOC_MAP_DIFFUSE RL_SHADER_LOC_MAP_ALBEDO +#define RL_SHADER_LOC_MAP_SPECULAR RL_SHADER_LOC_MAP_METALNESS + +// Shader uniform data type +typedef enum { + RL_SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float + RL_SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) + RL_SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) + RL_SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float) + RL_SHADER_UNIFORM_INT, // Shader uniform type: int + RL_SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) + RL_SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) + RL_SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) + RL_SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d +} rlShaderUniformDataType; + +// Shader attribute data types +typedef enum { + RL_SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float + RL_SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float) + RL_SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float) + RL_SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) +} rlShaderAttributeDataType; + +// Framebuffer attachment type +// NOTE: By default up to 8 color channels defined, but it can be more +typedef enum { + RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 + RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1 + RL_ATTACHMENT_COLOR_CHANNEL2, // Framebuffer attachment type: color 2 + RL_ATTACHMENT_COLOR_CHANNEL3, // Framebuffer attachment type: color 3 + RL_ATTACHMENT_COLOR_CHANNEL4, // Framebuffer attachment type: color 4 + RL_ATTACHMENT_COLOR_CHANNEL5, // Framebuffer attachment type: color 5 + RL_ATTACHMENT_COLOR_CHANNEL6, // Framebuffer attachment type: color 6 + RL_ATTACHMENT_COLOR_CHANNEL7, // Framebuffer attachment type: color 7 + RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth + RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil +} rlFramebufferAttachType; + +// Framebuffer texture attachment type +typedef enum { + RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_X, // Framebuffer texture attachment type: cubemap, -X side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Y, // Framebuffer texture attachment type: cubemap, +Y side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y, // Framebuffer texture attachment type: cubemap, -Y side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Z, // Framebuffer texture attachment type: cubemap, +Z side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z, // Framebuffer texture attachment type: cubemap, -Z side + RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d + RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer +} rlFramebufferAttachTextureType; + +// Face culling mode +typedef enum { + RL_CULL_FACE_FRONT = 0, + RL_CULL_FACE_BACK +} rlCullMode; + +//------------------------------------------------------------------------------------ +// Functions Declaration - Matrix operations +//------------------------------------------------------------------------------------ + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +RLAPI void rlMatrixMode(int mode); // Choose the current matrix to be transformed +RLAPI void rlPushMatrix(void); // Push the current matrix to stack +RLAPI void rlPopMatrix(void); // Pop latest inserted matrix from stack +RLAPI void rlLoadIdentity(void); // Reset current matrix to identity matrix +RLAPI void rlTranslatef(float x, float y, float z); // Multiply the current matrix by a translation matrix +RLAPI void rlRotatef(float angle, float x, float y, float z); // Multiply the current matrix by a rotation matrix +RLAPI void rlScalef(float x, float y, float z); // Multiply the current matrix by a scaling matrix +RLAPI void rlMultMatrixf(const float *matf); // Multiply the current matrix by another matrix +RLAPI void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar); +RLAPI void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar); +RLAPI void rlViewport(int x, int y, int width, int height); // Set the viewport area + +//------------------------------------------------------------------------------------ +// Functions Declaration - Vertex level operations +//------------------------------------------------------------------------------------ +RLAPI void rlBegin(int mode); // Initialize drawing mode (how to organize vertex) +RLAPI void rlEnd(void); // Finish vertex providing +RLAPI void rlVertex2i(int x, int y); // Define one vertex (position) - 2 int +RLAPI void rlVertex2f(float x, float y); // Define one vertex (position) - 2 float +RLAPI void rlVertex3f(float x, float y, float z); // Define one vertex (position) - 3 float +RLAPI void rlTexCoord2f(float x, float y); // Define one vertex (texture coordinate) - 2 float +RLAPI void rlNormal3f(float x, float y, float z); // Define one vertex (normal) - 3 float +RLAPI void rlColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a); // Define one vertex (color) - 4 byte +RLAPI void rlColor3f(float x, float y, float z); // Define one vertex (color) - 3 float +RLAPI void rlColor4f(float x, float y, float z, float w); // Define one vertex (color) - 4 float + +//------------------------------------------------------------------------------------ +// Functions Declaration - OpenGL style functions (common to 1.1, 3.3+, ES2) +// NOTE: This functions are used to completely abstract raylib code from OpenGL layer, +// some of them are direct wrappers over OpenGL calls, some others are custom +//------------------------------------------------------------------------------------ + +// Vertex buffers state +RLAPI bool rlEnableVertexArray(unsigned int vaoId); // Enable vertex array (VAO, if supported) +RLAPI void rlDisableVertexArray(void); // Disable vertex array (VAO, if supported) +RLAPI void rlEnableVertexBuffer(unsigned int id); // Enable vertex buffer (VBO) +RLAPI void rlDisableVertexBuffer(void); // Disable vertex buffer (VBO) +RLAPI void rlEnableVertexBufferElement(unsigned int id);// Enable vertex buffer element (VBO element) +RLAPI void rlDisableVertexBufferElement(void); // Disable vertex buffer element (VBO element) +RLAPI void rlEnableVertexAttribute(unsigned int index); // Enable vertex attribute index +RLAPI void rlDisableVertexAttribute(unsigned int index);// Disable vertex attribute index +#if defined(GRAPHICS_API_OPENGL_11) +RLAPI void rlEnableStatePointer(int vertexAttribType, void *buffer); // Enable attribute state pointer +RLAPI void rlDisableStatePointer(int vertexAttribType); // Disable attribute state pointer +#endif + +// Textures state +RLAPI void rlActiveTextureSlot(int slot); // Select and active a texture slot +RLAPI void rlEnableTexture(unsigned int id); // Enable texture +RLAPI void rlDisableTexture(void); // Disable texture +RLAPI void rlEnableTextureCubemap(unsigned int id); // Enable texture cubemap +RLAPI void rlDisableTextureCubemap(void); // Disable texture cubemap +RLAPI void rlTextureParameters(unsigned int id, int param, int value); // Set texture parameters (filter, wrap) +RLAPI void rlCubemapParameters(unsigned int id, int param, int value); // Set cubemap parameters (filter, wrap) + +// Shader state +RLAPI void rlEnableShader(unsigned int id); // Enable shader program +RLAPI void rlDisableShader(void); // Disable shader program + +// Framebuffer state +RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo) +RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer +RLAPI void rlActiveDrawBuffers(int count); // Activate multiple draw color buffers + +// General render state +RLAPI void rlEnableColorBlend(void); // Enable color blending +RLAPI void rlDisableColorBlend(void); // Disable color blending +RLAPI void rlEnableDepthTest(void); // Enable depth test +RLAPI void rlDisableDepthTest(void); // Disable depth test +RLAPI void rlEnableDepthMask(void); // Enable depth write +RLAPI void rlDisableDepthMask(void); // Disable depth write +RLAPI void rlEnableBackfaceCulling(void); // Enable backface culling +RLAPI void rlDisableBackfaceCulling(void); // Disable backface culling +RLAPI void rlSetCullFace(int mode); // Set face culling mode +RLAPI void rlEnableScissorTest(void); // Enable scissor test +RLAPI void rlDisableScissorTest(void); // Disable scissor test +RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test +RLAPI void rlEnableWireMode(void); // Enable wire mode +RLAPI void rlDisableWireMode(void); // Disable wire mode +RLAPI void rlSetLineWidth(float width); // Set the line drawing width +RLAPI float rlGetLineWidth(void); // Get the line drawing width +RLAPI void rlEnableSmoothLines(void); // Enable line aliasing +RLAPI void rlDisableSmoothLines(void); // Disable line aliasing +RLAPI void rlEnableStereoRender(void); // Enable stereo rendering +RLAPI void rlDisableStereoRender(void); // Disable stereo rendering +RLAPI bool rlIsStereoRenderEnabled(void); // Check if stereo render is enabled + +RLAPI void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a); // Clear color buffer with color +RLAPI void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) +RLAPI void rlCheckErrors(void); // Check and log OpenGL error codes +RLAPI void rlSetBlendMode(int mode); // Set blending mode +RLAPI void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation); // Set blending mode factor and equation (using OpenGL factors) +RLAPI void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha); // Set blending mode factors and equations separately (using OpenGL factors) + +//------------------------------------------------------------------------------------ +// Functions Declaration - rlgl functionality +//------------------------------------------------------------------------------------ +// rlgl initialization functions +RLAPI void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) +RLAPI void rlglClose(void); // De-initialize rlgl (buffers, shaders, textures) +RLAPI void rlLoadExtensions(void *loader); // Load OpenGL extensions (loader function required) +RLAPI int rlGetVersion(void); // Get current OpenGL version +RLAPI void rlSetFramebufferWidth(int width); // Set current framebuffer width +RLAPI int rlGetFramebufferWidth(void); // Get default framebuffer width +RLAPI void rlSetFramebufferHeight(int height); // Set current framebuffer height +RLAPI int rlGetFramebufferHeight(void); // Get default framebuffer height + +RLAPI unsigned int rlGetTextureIdDefault(void); // Get default texture id +RLAPI unsigned int rlGetShaderIdDefault(void); // Get default shader id +RLAPI int *rlGetShaderLocsDefault(void); // Get default shader locations + +// Render batch management +// NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode +// but this render batch API is exposed in case of custom batches are required +RLAPI rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements); // Load a render batch system +RLAPI void rlUnloadRenderBatch(rlRenderBatch batch); // Unload render batch system +RLAPI void rlDrawRenderBatch(rlRenderBatch *batch); // Draw render batch data (Update->Draw->Reset) +RLAPI void rlSetRenderBatchActive(rlRenderBatch *batch); // Set the active render batch for rlgl (NULL for default internal) +RLAPI void rlDrawRenderBatchActive(void); // Update and draw internal render batch +RLAPI bool rlCheckRenderBatchLimit(int vCount); // Check internal buffer overflow for a given number of vertex + +RLAPI void rlSetTexture(unsigned int id); // Set current texture for render batch and check buffers limits + +//------------------------------------------------------------------------------------------------------------------------ + +// Vertex buffers management +RLAPI unsigned int rlLoadVertexArray(void); // Load vertex array (vao) if supported +RLAPI unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic); // Load a vertex buffer attribute +RLAPI unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic); // Load a new attributes element buffer +RLAPI void rlUpdateVertexBuffer(unsigned int bufferId, const void *data, int dataSize, int offset); // Update GPU buffer with new data +RLAPI void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset); // Update vertex buffer elements with new data +RLAPI void rlUnloadVertexArray(unsigned int vaoId); +RLAPI void rlUnloadVertexBuffer(unsigned int vboId); +RLAPI void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, const void *pointer); +RLAPI void rlSetVertexAttributeDivisor(unsigned int index, int divisor); +RLAPI void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count); // Set vertex attribute default value +RLAPI void rlDrawVertexArray(int offset, int count); +RLAPI void rlDrawVertexArrayElements(int offset, int count, const void *buffer); +RLAPI void rlDrawVertexArrayInstanced(int offset, int count, int instances); +RLAPI void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances); + +// Textures management +RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU +RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo) +RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format); // Load texture cubemap +RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update GPU texture with new data +RLAPI void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats +RLAPI const char *rlGetPixelFormatName(unsigned int format); // Get name string for pixel format +RLAPI void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory +RLAPI void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps); // Generate mipmap data for selected texture +RLAPI void *rlReadTexturePixels(unsigned int id, int width, int height, int format); // Read texture pixel data +RLAPI unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) + +// Framebuffer management (fbo) +RLAPI unsigned int rlLoadFramebuffer(int width, int height); // Load an empty framebuffer +RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer +RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete +RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU + +// Shaders management +RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings +RLAPI unsigned int rlCompileShader(const char *shaderCode, int type); // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER) +RLAPI unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program +RLAPI void rlUnloadShaderProgram(unsigned int id); // Unload shader program +RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName); // Get shader location uniform +RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName); // Get shader location attribute +RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count); // Set shader value uniform +RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat); // Set shader value matrix +RLAPI void rlSetUniformSampler(int locIndex, unsigned int textureId); // Set shader value sampler +RLAPI void rlSetShader(unsigned int id, int *locs); // Set shader currently active (id and locations) + +// Compute shader management +RLAPI unsigned int rlLoadComputeShaderProgram(unsigned int shaderId); // Load compute shader program +RLAPI void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ); // Dispatch compute shader (equivalent to *draw* for graphics pipeline) + +// Shader buffer storage object management (ssbo) +RLAPI unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHint); // Load shader storage buffer object (SSBO) +RLAPI void rlUnloadShaderBuffer(unsigned int ssboId); // Unload shader storage buffer object (SSBO) +RLAPI void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSize, unsigned int offset); // Update SSBO buffer data +RLAPI void rlBindShaderBuffer(unsigned int id, unsigned int index); // Bind SSBO buffer +RLAPI void rlReadShaderBuffer(unsigned int id, void *dest, unsigned int count, unsigned int offset); // Read SSBO buffer data (GPU->CPU) +RLAPI void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned int destOffset, unsigned int srcOffset, unsigned int count); // Copy SSBO data between buffers +RLAPI unsigned int rlGetShaderBufferSize(unsigned int id); // Get SSBO buffer size + +// Buffer management +RLAPI void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool readonly); // Bind image texture + +// Matrix state management +RLAPI Matrix rlGetMatrixModelview(void); // Get internal modelview matrix +RLAPI Matrix rlGetMatrixProjection(void); // Get internal projection matrix +RLAPI Matrix rlGetMatrixTransform(void); // Get internal accumulated transform matrix +RLAPI Matrix rlGetMatrixProjectionStereo(int eye); // Get internal projection matrix for stereo render (selected eye) +RLAPI Matrix rlGetMatrixViewOffsetStereo(int eye); // Get internal view offset matrix for stereo render (selected eye) +RLAPI void rlSetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) +RLAPI void rlSetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) +RLAPI void rlSetMatrixProjectionStereo(Matrix right, Matrix left); // Set eyes projection matrices for stereo rendering +RLAPI void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left); // Set eyes view offsets matrices for stereo rendering + +// Quick and dirty cube/quad buffers load->draw->unload +RLAPI void rlLoadDrawCube(void); // Load and draw a cube +RLAPI void rlLoadDrawQuad(void); // Load and draw a quad + +#if defined(__cplusplus) +} +#endif + +#endif // RLGL_H + +/*********************************************************************************** +* +* RLGL IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RLGL_IMPLEMENTATION) + +#if defined(GRAPHICS_API_OPENGL_11) + #if defined(__APPLE__) + #include // OpenGL 1.1 library for OSX + #include // OpenGL extensions library + #else + // APIENTRY for OpenGL function pointer declarations is required + #if !defined(APIENTRY) + #if defined(_WIN32) + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif + #endif + // WINGDIAPI definition. Some Windows OpenGL headers need it + #if !defined(WINGDIAPI) && defined(_WIN32) + #define WINGDIAPI __declspec(dllimport) + #endif + + #include // OpenGL 1.1 library + #endif +#endif + +#if defined(GRAPHICS_API_OPENGL_33) + #define GLAD_MALLOC RL_MALLOC + #define GLAD_FREE RL_FREE + + #define GLAD_GL_IMPLEMENTATION + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, + // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 + #if defined(PLATFORM_DESKTOP) + #define GLAD_GLES2_IMPLEMENTATION + #include "external/glad_gles2.h" + #else + #define GL_GLEXT_PROTOTYPES + //#include // EGL library -> not required, platform layer + #include // OpenGL ES 2.0 library + #include // OpenGL ES 2.0 extensions library + #endif + + // It seems OpenGL ES 2.0 instancing entry points are not defined on Raspberry Pi + // provided headers (despite being defined in official Khronos GLES2 headers) + #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) + typedef void (GL_APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); + typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); + typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); + #endif +#endif + +#include // Required for: malloc(), free() +#include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] +#include // Required for: sqrtf(), sinf(), cosf(), floor(), log() + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef PI + #define PI 3.14159265358979323846f +#endif +#ifndef DEG2RAD + #define DEG2RAD (PI/180.0f) +#endif +#ifndef RAD2DEG + #define RAD2DEG (180.0f/PI) +#endif + +#ifndef GL_SHADING_LANGUAGE_VERSION + #define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#endif + +#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#endif +#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif +#ifndef GL_ETC1_RGB8_OES + #define GL_ETC1_RGB8_OES 0x8D64 +#endif +#ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 +#endif +#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif +#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#endif +#ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR + #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0 +#endif +#ifndef GL_COMPRESSED_RGBA_ASTC_8x8_KHR + #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7 +#endif + +#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif +#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + #define GL_UNSIGNED_SHORT_5_6_5 0x8363 + #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 + #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#endif + +#if defined(GRAPHICS_API_OPENGL_21) + #define GL_LUMINANCE 0x1909 + #define GL_LUMINANCE_ALPHA 0x190A +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + #define glClearDepth glClearDepthf + #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER + #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER +#endif + +// Default shader vertex attribute names to set location points +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION + #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL + #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR + #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 +#endif +#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 + #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5 +#endif + +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP + #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW + #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION + #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL + #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL + #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) +#endif +#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR + #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +#endif +#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 + #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +#endif +#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 + #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) +#endif +#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 + #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +typedef struct rlglData { + rlRenderBatch *currentBatch; // Current render batch + rlRenderBatch defaultBatch; // Default internal render batch + + struct { + int vertexCounter; // Current active render batch vertex counter (generic, used for all batches) + float texcoordx, texcoordy; // Current active texture coordinate (added on glVertex*()) + float normalx, normaly, normalz; // Current active normal (added on glVertex*()) + unsigned char colorr, colorg, colorb, colora; // Current active color (added on glVertex*()) + + int currentMatrixMode; // Current matrix mode + Matrix *currentMatrix; // Current matrix pointer + Matrix modelview; // Default modelview matrix + Matrix projection; // Default projection matrix + Matrix transform; // Transform matrix to be used with rlTranslate, rlRotate, rlScale + bool transformRequired; // Require transform matrix application to current draw-call vertex (if required) + Matrix stack[RL_MAX_MATRIX_STACK_SIZE];// Matrix stack for push/pop + int stackCounter; // Matrix stack counter + + unsigned int defaultTextureId; // Default texture used on shapes/poly drawing (required by shader) + unsigned int activeTextureId[RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS]; // Active texture ids to be enabled on batch drawing (0 active by default) + unsigned int defaultVShaderId; // Default vertex shader id (used by default shader program) + unsigned int defaultFShaderId; // Default fragment shader id (used by default shader program) + unsigned int defaultShaderId; // Default shader program id, supports vertex color and diffuse texture + int *defaultShaderLocs; // Default shader locations pointer to be used on rendering + unsigned int currentShaderId; // Current shader id to be used on rendering (by default, defaultShaderId) + int *currentShaderLocs; // Current shader locations pointer to be used on rendering (by default, defaultShaderLocs) + + bool stereoRender; // Stereo rendering flag + Matrix projectionStereo[2]; // VR stereo rendering eyes projection matrices + Matrix viewOffsetStereo[2]; // VR stereo rendering eyes view offset matrices + + // Blending variables + int currentBlendMode; // Blending mode active + int glBlendSrcFactor; // Blending source factor + int glBlendDstFactor; // Blending destination factor + int glBlendEquation; // Blending equation + int glBlendSrcFactorRGB; // Blending source RGB factor + int glBlendDestFactorRGB; // Blending destination RGB factor + int glBlendSrcFactorAlpha; // Blending source alpha factor + int glBlendDestFactorAlpha; // Blending destination alpha factor + int glBlendEquationRGB; // Blending equation for RGB + int glBlendEquationAlpha; // Blending equation for alpha + bool glCustomBlendModeModified; // Custom blending factor and equation modification status + + int framebufferWidth; // Current framebuffer width + int framebufferHeight; // Current framebuffer height + + } State; // Renderer state + struct { + bool vao; // VAO support (OpenGL ES2 could not support VAO extension) (GL_ARB_vertex_array_object) + bool instancing; // Instancing supported (GL_ANGLE_instanced_arrays, GL_EXT_draw_instanced + GL_EXT_instanced_arrays) + bool texNPOT; // NPOT textures full support (GL_ARB_texture_non_power_of_two, GL_OES_texture_npot) + bool texDepth; // Depth textures supported (GL_ARB_depth_texture, GL_OES_depth_texture) + bool texDepthWebGL; // Depth textures supported WebGL specific (GL_WEBGL_depth_texture) + bool texFloat32; // float textures support (32 bit per channel) (GL_OES_texture_float) + bool texCompDXT; // DDS texture compression support (GL_EXT_texture_compression_s3tc, GL_WEBGL_compressed_texture_s3tc, GL_WEBKIT_WEBGL_compressed_texture_s3tc) + bool texCompETC1; // ETC1 texture compression support (GL_OES_compressed_ETC1_RGB8_texture, GL_WEBGL_compressed_texture_etc1) + bool texCompETC2; // ETC2/EAC texture compression support (GL_ARB_ES3_compatibility) + bool texCompPVRT; // PVR texture compression support (GL_IMG_texture_compression_pvrtc) + bool texCompASTC; // ASTC texture compression support (GL_KHR_texture_compression_astc_hdr, GL_KHR_texture_compression_astc_ldr) + bool texMirrorClamp; // Clamp mirror wrap mode supported (GL_EXT_texture_mirror_clamp) + bool texAnisoFilter; // Anisotropic texture filtering support (GL_EXT_texture_filter_anisotropic) + bool computeShader; // Compute shaders support (GL_ARB_compute_shader) + bool ssbo; // Shader storage buffer object support (GL_ARB_shader_storage_buffer_object) + + float maxAnisotropyLevel; // Maximum anisotropy level supported (minimum is 2.0f) + int maxDepthBits; // Maximum bits for depth component + + } ExtSupported; // Extensions supported flags +} rlglData; + +typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions loader signature (same as GLADloadproc) + +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +static rlglData RLGL = { 0 }; +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +#if defined(GRAPHICS_API_OPENGL_ES2) +// NOTE: VAO functionality is exposed through extensions (OES) +static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL; +static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL; +static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL; + +// NOTE: Instancing functionality could also be available through extension +static PFNGLDRAWARRAYSINSTANCEDEXTPROC glDrawArraysInstanced = NULL; +static PFNGLDRAWELEMENTSINSTANCEDEXTPROC glDrawElementsInstanced = NULL; +static PFNGLVERTEXATTRIBDIVISOREXTPROC glVertexAttribDivisor = NULL; +#endif + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +static void rlLoadShaderDefault(void); // Load default shader +static void rlUnloadShaderDefault(void); // Unload default shader +#if defined(RLGL_SHOW_GL_DETAILS_INFO) +static char *rlGetCompressedFormatName(int format); // Get compressed format official GL identifier name +#endif // RLGL_SHOW_GL_DETAILS_INFO +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +static int rlGetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture) + +// Auxiliar matrix math functions +static Matrix rlMatrixIdentity(void); // Get identity matrix +static Matrix rlMatrixMultiply(Matrix left, Matrix right); // Multiply two matrices + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Matrix operations +//---------------------------------------------------------------------------------- + +#if defined(GRAPHICS_API_OPENGL_11) +// Fallback to OpenGL 1.1 function calls +//--------------------------------------- +void rlMatrixMode(int mode) +{ + switch (mode) + { + case RL_PROJECTION: glMatrixMode(GL_PROJECTION); break; + case RL_MODELVIEW: glMatrixMode(GL_MODELVIEW); break; + case RL_TEXTURE: glMatrixMode(GL_TEXTURE); break; + default: break; + } +} + +void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar) +{ + glFrustum(left, right, bottom, top, znear, zfar); +} + +void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar) +{ + glOrtho(left, right, bottom, top, znear, zfar); +} + +void rlPushMatrix(void) { glPushMatrix(); } +void rlPopMatrix(void) { glPopMatrix(); } +void rlLoadIdentity(void) { glLoadIdentity(); } +void rlTranslatef(float x, float y, float z) { glTranslatef(x, y, z); } +void rlRotatef(float angle, float x, float y, float z) { glRotatef(angle, x, y, z); } +void rlScalef(float x, float y, float z) { glScalef(x, y, z); } +void rlMultMatrixf(const float *matf) { glMultMatrixf(matf); } +#endif +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Choose the current matrix to be transformed +void rlMatrixMode(int mode) +{ + if (mode == RL_PROJECTION) RLGL.State.currentMatrix = &RLGL.State.projection; + else if (mode == RL_MODELVIEW) RLGL.State.currentMatrix = &RLGL.State.modelview; + //else if (mode == RL_TEXTURE) // Not supported + + RLGL.State.currentMatrixMode = mode; +} + +// Push the current matrix into RLGL.State.stack +void rlPushMatrix(void) +{ + if (RLGL.State.stackCounter >= RL_MAX_MATRIX_STACK_SIZE) TRACELOG(RL_LOG_ERROR, "RLGL: Matrix stack overflow (RL_MAX_MATRIX_STACK_SIZE)"); + + if (RLGL.State.currentMatrixMode == RL_MODELVIEW) + { + RLGL.State.transformRequired = true; + RLGL.State.currentMatrix = &RLGL.State.transform; + } + + RLGL.State.stack[RLGL.State.stackCounter] = *RLGL.State.currentMatrix; + RLGL.State.stackCounter++; +} + +// Pop lattest inserted matrix from RLGL.State.stack +void rlPopMatrix(void) +{ + if (RLGL.State.stackCounter > 0) + { + Matrix mat = RLGL.State.stack[RLGL.State.stackCounter - 1]; + *RLGL.State.currentMatrix = mat; + RLGL.State.stackCounter--; + } + + if ((RLGL.State.stackCounter == 0) && (RLGL.State.currentMatrixMode == RL_MODELVIEW)) + { + RLGL.State.currentMatrix = &RLGL.State.modelview; + RLGL.State.transformRequired = false; + } +} + +// Reset current matrix to identity matrix +void rlLoadIdentity(void) +{ + *RLGL.State.currentMatrix = rlMatrixIdentity(); +} + +// Multiply the current matrix by a translation matrix +void rlTranslatef(float x, float y, float z) +{ + Matrix matTranslation = { + 1.0f, 0.0f, 0.0f, x, + 0.0f, 1.0f, 0.0f, y, + 0.0f, 0.0f, 1.0f, z, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + // NOTE: We transpose matrix with multiplication order + *RLGL.State.currentMatrix = rlMatrixMultiply(matTranslation, *RLGL.State.currentMatrix); +} + +// Multiply the current matrix by a rotation matrix +// NOTE: The provided angle must be in degrees +void rlRotatef(float angle, float x, float y, float z) +{ + Matrix matRotation = rlMatrixIdentity(); + + // Axis vector (x, y, z) normalization + float lengthSquared = x*x + y*y + z*z; + if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) + { + float inverseLength = 1.0f/sqrtf(lengthSquared); + x *= inverseLength; + y *= inverseLength; + z *= inverseLength; + } + + // Rotation matrix generation + float sinres = sinf(DEG2RAD*angle); + float cosres = cosf(DEG2RAD*angle); + float t = 1.0f - cosres; + + matRotation.m0 = x*x*t + cosres; + matRotation.m1 = y*x*t + z*sinres; + matRotation.m2 = z*x*t - y*sinres; + matRotation.m3 = 0.0f; + + matRotation.m4 = x*y*t - z*sinres; + matRotation.m5 = y*y*t + cosres; + matRotation.m6 = z*y*t + x*sinres; + matRotation.m7 = 0.0f; + + matRotation.m8 = x*z*t + y*sinres; + matRotation.m9 = y*z*t - x*sinres; + matRotation.m10 = z*z*t + cosres; + matRotation.m11 = 0.0f; + + matRotation.m12 = 0.0f; + matRotation.m13 = 0.0f; + matRotation.m14 = 0.0f; + matRotation.m15 = 1.0f; + + // NOTE: We transpose matrix with multiplication order + *RLGL.State.currentMatrix = rlMatrixMultiply(matRotation, *RLGL.State.currentMatrix); +} + +// Multiply the current matrix by a scaling matrix +void rlScalef(float x, float y, float z) +{ + Matrix matScale = { + x, 0.0f, 0.0f, 0.0f, + 0.0f, y, 0.0f, 0.0f, + 0.0f, 0.0f, z, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + // NOTE: We transpose matrix with multiplication order + *RLGL.State.currentMatrix = rlMatrixMultiply(matScale, *RLGL.State.currentMatrix); +} + +// Multiply the current matrix by another matrix +void rlMultMatrixf(const float *matf) +{ + // Matrix creation from array + Matrix mat = { matf[0], matf[4], matf[8], matf[12], + matf[1], matf[5], matf[9], matf[13], + matf[2], matf[6], matf[10], matf[14], + matf[3], matf[7], matf[11], matf[15] }; + + *RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, mat); +} + +// Multiply the current matrix by a perspective matrix generated by parameters +void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar) +{ + Matrix matFrustum = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(zfar - znear); + + matFrustum.m0 = ((float) znear*2.0f)/rl; + matFrustum.m1 = 0.0f; + matFrustum.m2 = 0.0f; + matFrustum.m3 = 0.0f; + + matFrustum.m4 = 0.0f; + matFrustum.m5 = ((float) znear*2.0f)/tb; + matFrustum.m6 = 0.0f; + matFrustum.m7 = 0.0f; + + matFrustum.m8 = ((float)right + (float)left)/rl; + matFrustum.m9 = ((float)top + (float)bottom)/tb; + matFrustum.m10 = -((float)zfar + (float)znear)/fn; + matFrustum.m11 = -1.0f; + + matFrustum.m12 = 0.0f; + matFrustum.m13 = 0.0f; + matFrustum.m14 = -((float)zfar*(float)znear*2.0f)/fn; + matFrustum.m15 = 0.0f; + + *RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, matFrustum); +} + +// Multiply the current matrix by an orthographic matrix generated by parameters +void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar) +{ + // NOTE: If left-right and top-botton values are equal it could create a division by zero, + // response to it is platform/compiler dependant + Matrix matOrtho = { 0 }; + + float rl = (float)(right - left); + float tb = (float)(top - bottom); + float fn = (float)(zfar - znear); + + matOrtho.m0 = 2.0f/rl; + matOrtho.m1 = 0.0f; + matOrtho.m2 = 0.0f; + matOrtho.m3 = 0.0f; + matOrtho.m4 = 0.0f; + matOrtho.m5 = 2.0f/tb; + matOrtho.m6 = 0.0f; + matOrtho.m7 = 0.0f; + matOrtho.m8 = 0.0f; + matOrtho.m9 = 0.0f; + matOrtho.m10 = -2.0f/fn; + matOrtho.m11 = 0.0f; + matOrtho.m12 = -((float)left + (float)right)/rl; + matOrtho.m13 = -((float)top + (float)bottom)/tb; + matOrtho.m14 = -((float)zfar + (float)znear)/fn; + matOrtho.m15 = 1.0f; + + *RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, matOrtho); +} +#endif + +// Set the viewport area (transformation from normalized device coordinates to window coordinates) +// NOTE: We store current viewport dimensions +void rlViewport(int x, int y, int width, int height) +{ + glViewport(x, y, width, height); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Vertex level operations +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_11) +// Fallback to OpenGL 1.1 function calls +//--------------------------------------- +void rlBegin(int mode) +{ + switch (mode) + { + case RL_LINES: glBegin(GL_LINES); break; + case RL_TRIANGLES: glBegin(GL_TRIANGLES); break; + case RL_QUADS: glBegin(GL_QUADS); break; + default: break; + } +} + +void rlEnd() { glEnd(); } +void rlVertex2i(int x, int y) { glVertex2i(x, y); } +void rlVertex2f(float x, float y) { glVertex2f(x, y); } +void rlVertex3f(float x, float y, float z) { glVertex3f(x, y, z); } +void rlTexCoord2f(float x, float y) { glTexCoord2f(x, y); } +void rlNormal3f(float x, float y, float z) { glNormal3f(x, y, z); } +void rlColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { glColor4ub(r, g, b, a); } +void rlColor3f(float x, float y, float z) { glColor3f(x, y, z); } +void rlColor4f(float x, float y, float z, float w) { glColor4f(x, y, z, w); } +#endif +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Initialize drawing mode (how to organize vertex) +void rlBegin(int mode) +{ + // Draw mode can be RL_LINES, RL_TRIANGLES and RL_QUADS + // NOTE: In all three cases, vertex are accumulated over default internal vertex buffer + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode != mode) + { + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount > 0) + { + // Make sure current RLGL.currentBatch->draws[i].vertexCount is aligned a multiple of 4, + // that way, following QUADS drawing will keep aligned with index processing + // It implies adding some extra alignment vertex at the end of the draw, + // those vertex are not processed but they are considered as an additional offset + // for the next set of vertex to be drawn + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount : RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4); + else if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? 1 : (4 - (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4))); + else RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = 0; + + if (!rlCheckRenderBatchLimit(RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment)) + { + RLGL.State.vertexCounter += RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment; + RLGL.currentBatch->drawCounter++; + } + } + + if (RLGL.currentBatch->drawCounter >= RL_DEFAULT_BATCH_DRAWCALLS) rlDrawRenderBatch(RLGL.currentBatch); + + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = mode; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount = 0; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = RLGL.State.defaultTextureId; + } +} + +// Finish vertex providing +void rlEnd(void) +{ + // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, + // as well as depth buffer bit-depth (16bit or 24bit or 32bit) + // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) + RLGL.currentBatch->currentDepth += (1.0f/20000.0f); +} + +// Define one vertex (position) +// NOTE: Vertex position data is the basic information required for drawing +void rlVertex3f(float x, float y, float z) +{ + float tx = x; + float ty = y; + float tz = z; + + // Transform provided vector if required + if (RLGL.State.transformRequired) + { + tx = RLGL.State.transform.m0*x + RLGL.State.transform.m4*y + RLGL.State.transform.m8*z + RLGL.State.transform.m12; + ty = RLGL.State.transform.m1*x + RLGL.State.transform.m5*y + RLGL.State.transform.m9*z + RLGL.State.transform.m13; + tz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z + RLGL.State.transform.m14; + } + + // WARNING: We can't break primitives when launching a new batch. + // RL_LINES comes in pairs, RL_TRIANGLES come in groups of 3 vertices and RL_QUADS come in groups of 4 vertices. + // We must check current draw.mode when a new vertex is required and finish the batch only if the draw.mode draw.vertexCount is %2, %3 or %4 + if (RLGL.State.vertexCounter > (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4 - 4)) + { + if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) && + (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%2 == 0)) + { + // Reached the maximum number of vertices for RL_LINES drawing + // Launch a draw call but keep current state for next vertices comming + // NOTE: We add +1 vertex to the check for security + rlCheckRenderBatchLimit(2 + 1); + } + else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) && + (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%3 == 0)) + { + rlCheckRenderBatchLimit(3 + 1); + } + else if ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_QUADS) && + (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4 == 0)) + { + rlCheckRenderBatchLimit(4 + 1); + } + } + + // Add vertices + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter] = tx; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 1] = ty; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].vertices[3*RLGL.State.vertexCounter + 2] = tz; + + // Add current texcoord + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter] = RLGL.State.texcoordx; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter + 1] = RLGL.State.texcoordy; + + // TODO: Add current normal + // By default rlVertexBuffer type does not store normals + + // Add current color + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter] = RLGL.State.colorr; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 1] = RLGL.State.colorg; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 2] = RLGL.State.colorb; + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter + 3] = RLGL.State.colora; + + RLGL.State.vertexCounter++; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount++; +} + +// Define one vertex (position) +void rlVertex2f(float x, float y) +{ + rlVertex3f(x, y, RLGL.currentBatch->currentDepth); +} + +// Define one vertex (position) +void rlVertex2i(int x, int y) +{ + rlVertex3f((float)x, (float)y, RLGL.currentBatch->currentDepth); +} + +// Define one vertex (texture coordinate) +// NOTE: Texture coordinates are limited to QUADS only +void rlTexCoord2f(float x, float y) +{ + RLGL.State.texcoordx = x; + RLGL.State.texcoordy = y; +} + +// Define one vertex (normal) +// NOTE: Normals limited to TRIANGLES only? +void rlNormal3f(float x, float y, float z) +{ + RLGL.State.normalx = x; + RLGL.State.normaly = y; + RLGL.State.normalz = z; +} + +// Define one vertex (color) +void rlColor4ub(unsigned char x, unsigned char y, unsigned char z, unsigned char w) +{ + RLGL.State.colorr = x; + RLGL.State.colorg = y; + RLGL.State.colorb = z; + RLGL.State.colora = w; +} + +// Define one vertex (color) +void rlColor4f(float r, float g, float b, float a) +{ + rlColor4ub((unsigned char)(r*255), (unsigned char)(g*255), (unsigned char)(b*255), (unsigned char)(a*255)); +} + +// Define one vertex (color) +void rlColor3f(float x, float y, float z) +{ + rlColor4ub((unsigned char)(x*255), (unsigned char)(y*255), (unsigned char)(z*255), 255); +} + +#endif + +//-------------------------------------------------------------------------------------- +// Module Functions Definition - OpenGL style functions (common to 1.1, 3.3+, ES2) +//-------------------------------------------------------------------------------------- + +// Set current texture to use +void rlSetTexture(unsigned int id) +{ + if (id == 0) + { +#if defined(GRAPHICS_API_OPENGL_11) + rlDisableTexture(); +#else + // NOTE: If quads batch limit is reached, we force a draw call and next batch starts + if (RLGL.State.vertexCounter >= + RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4) + { + rlDrawRenderBatch(RLGL.currentBatch); + } +#endif + } + else + { +#if defined(GRAPHICS_API_OPENGL_11) + rlEnableTexture(id); +#else + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId != id) + { + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount > 0) + { + // Make sure current RLGL.currentBatch->draws[i].vertexCount is aligned a multiple of 4, + // that way, following QUADS drawing will keep aligned with index processing + // It implies adding some extra alignment vertex at the end of the draw, + // those vertex are not processed but they are considered as an additional offset + // for the next set of vertex to be drawn + if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_LINES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount : RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4); + else if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode == RL_TRIANGLES) RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = ((RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount < 4)? 1 : (4 - (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount%4))); + else RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment = 0; + + if (!rlCheckRenderBatchLimit(RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment)) + { + RLGL.State.vertexCounter += RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment; + + RLGL.currentBatch->drawCounter++; + } + } + + if (RLGL.currentBatch->drawCounter >= RL_DEFAULT_BATCH_DRAWCALLS) rlDrawRenderBatch(RLGL.currentBatch); + + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = id; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount = 0; + } +#endif + } +} + +// Select and active a texture slot +void rlActiveTextureSlot(int slot) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glActiveTexture(GL_TEXTURE0 + slot); +#endif +} + +// Enable texture +void rlEnableTexture(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_TEXTURE_2D); +#endif + glBindTexture(GL_TEXTURE_2D, id); +} + +// Disable texture +void rlDisableTexture(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glDisable(GL_TEXTURE_2D); +#endif + glBindTexture(GL_TEXTURE_2D, 0); +} + +// Enable texture cubemap +void rlEnableTextureCubemap(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindTexture(GL_TEXTURE_CUBE_MAP, id); +#endif +} + +// Disable texture cubemap +void rlDisableTextureCubemap(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif +} + +// Set texture parameters (wrap mode/filter mode) +void rlTextureParameters(unsigned int id, int param, int value) +{ + glBindTexture(GL_TEXTURE_2D, id); + +#if !defined(GRAPHICS_API_OPENGL_11) + // Reset anisotropy filter, in case it was set + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); +#endif + + switch (param) + { + case RL_TEXTURE_WRAP_S: + case RL_TEXTURE_WRAP_T: + { + if (value == RL_TEXTURE_WRAP_MIRROR_CLAMP) + { +#if !defined(GRAPHICS_API_OPENGL_11) + if (RLGL.ExtSupported.texMirrorClamp) glTexParameteri(GL_TEXTURE_2D, param, value); + else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); +#endif + } + else glTexParameteri(GL_TEXTURE_2D, param, value); + + } break; + case RL_TEXTURE_MAG_FILTER: + case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break; + case RL_TEXTURE_FILTER_ANISOTROPIC: + { +#if !defined(GRAPHICS_API_OPENGL_11) + if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f) + { + TRACELOG(RL_LOG_WARNING, "GL: Maximum anisotropic filter level supported is %iX", id, (int)RLGL.ExtSupported.maxAnisotropyLevel); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + } + else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); +#endif + } break; +#if defined(GRAPHICS_API_OPENGL_33) + case RL_TEXTURE_MIPMAP_BIAS_RATIO: glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, value/100.0f); +#endif + default: break; + } + + glBindTexture(GL_TEXTURE_2D, 0); +} + +// Set cubemap parameters (wrap mode/filter mode) +void rlCubemapParameters(unsigned int id, int param, int value) +{ +#if !defined(GRAPHICS_API_OPENGL_11) + glBindTexture(GL_TEXTURE_CUBE_MAP, id); + + // Reset anisotropy filter, in case it was set + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + + switch (param) + { + case RL_TEXTURE_WRAP_S: + case RL_TEXTURE_WRAP_T: + { + if (value == RL_TEXTURE_WRAP_MIRROR_CLAMP) + { + if (RLGL.ExtSupported.texMirrorClamp) glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); + else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); + } + else glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); + + } break; + case RL_TEXTURE_MAG_FILTER: + case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); break; + case RL_TEXTURE_FILTER_ANISOTROPIC: + { + if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f) + { + TRACELOG(RL_LOG_WARNING, "GL: Maximum anisotropic filter level supported is %iX", id, (int)RLGL.ExtSupported.maxAnisotropyLevel); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); + } + else TRACELOG(RL_LOG_WARNING, "GL: Anisotropic filtering not supported"); + } break; +#if defined(GRAPHICS_API_OPENGL_33) + case RL_TEXTURE_MIPMAP_BIAS_RATIO: glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_LOD_BIAS, value/100.0f); +#endif + default: break; + } + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif +} + +// Enable shader program +void rlEnableShader(unsigned int id) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + glUseProgram(id); +#endif +} + +// Disable shader program +void rlDisableShader(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + glUseProgram(0); +#endif +} + +// Enable rendering to texture (fbo) +void rlEnableFramebuffer(unsigned int id) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, id); +#endif +} + +// Disable rendering to texture +void rlDisableFramebuffer(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif +} + +// Activate multiple draw color buffers +// NOTE: One color buffer is always active by default +void rlActiveDrawBuffers(int count) +{ +#if (defined(GRAPHICS_API_OPENGL_33) && defined(RLGL_RENDER_TEXTURES_HINT)) + // NOTE: Maximum number of draw buffers supported is implementation dependant, + // it can be queried with glGet*() but it must be at least 8 + //GLint maxDrawBuffers = 0; + //glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); + + if (count > 0) + { + if (count > 8) TRACELOG(LOG_WARNING, "GL: Max color buffers limited to 8"); + else + { + unsigned int buffers[8] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3, + GL_COLOR_ATTACHMENT4, + GL_COLOR_ATTACHMENT5, + GL_COLOR_ATTACHMENT6, + GL_COLOR_ATTACHMENT7, + }; + + glDrawBuffers(count, buffers); + } + } + else TRACELOG(LOG_WARNING, "GL: One color buffer active by default"); +#endif +} + +//---------------------------------------------------------------------------------- +// General render state configuration +//---------------------------------------------------------------------------------- + +// Enable color blending +void rlEnableColorBlend(void) { glEnable(GL_BLEND); } + +// Disable color blending +void rlDisableColorBlend(void) { glDisable(GL_BLEND); } + +// Enable depth test +void rlEnableDepthTest(void) { glEnable(GL_DEPTH_TEST); } + +// Disable depth test +void rlDisableDepthTest(void) { glDisable(GL_DEPTH_TEST); } + +// Enable depth write +void rlEnableDepthMask(void) { glDepthMask(GL_TRUE); } + +// Disable depth write +void rlDisableDepthMask(void) { glDepthMask(GL_FALSE); } + +// Enable backface culling +void rlEnableBackfaceCulling(void) { glEnable(GL_CULL_FACE); } + +// Disable backface culling +void rlDisableBackfaceCulling(void) { glDisable(GL_CULL_FACE); } + +// Set face culling mode +void rlSetCullFace(int mode) +{ + switch (mode) + { + case RL_CULL_FACE_BACK: glCullFace(GL_BACK); break; + case RL_CULL_FACE_FRONT: glCullFace(GL_FRONT); break; + default: break; + } +} + +// Enable scissor test +void rlEnableScissorTest(void) { glEnable(GL_SCISSOR_TEST); } + +// Disable scissor test +void rlDisableScissorTest(void) { glDisable(GL_SCISSOR_TEST); } + +// Scissor test +void rlScissor(int x, int y, int width, int height) { glScissor(x, y, width, height); } + +// Enable wire mode +void rlEnableWireMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#endif +} + +// Disable wire mode +void rlDisableWireMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif +} + +// Set the line drawing width +void rlSetLineWidth(float width) { glLineWidth(width); } + +// Get the line drawing width +float rlGetLineWidth(void) +{ + float width = 0; + glGetFloatv(GL_LINE_WIDTH, &width); + return width; +} + +// Enable line aliasing +void rlEnableSmoothLines(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_11) + glEnable(GL_LINE_SMOOTH); +#endif +} + +// Disable line aliasing +void rlDisableSmoothLines(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_11) + glDisable(GL_LINE_SMOOTH); +#endif +} + +// Enable stereo rendering +void rlEnableStereoRender(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + RLGL.State.stereoRender = true; +#endif +} + +// Disable stereo rendering +void rlDisableStereoRender(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + RLGL.State.stereoRender = false; +#endif +} + +// Check if stereo render is enabled +bool rlIsStereoRenderEnabled(void) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) + return RLGL.State.stereoRender; +#else + return false; +#endif +} + +// Clear color buffer with color +void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + // Color values clamp to 0.0f(0) and 1.0f(255) + float cr = (float)r/255; + float cg = (float)g/255; + float cb = (float)b/255; + float ca = (float)a/255; + + glClearColor(cr, cg, cb, ca); +} + +// Clear used screen buffers (color and depth) +void rlClearScreenBuffers(void) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers: Color and Depth (Depth is used for 3D) + //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Stencil buffer not used... +} + +// Check and log OpenGL error codes +void rlCheckErrors() +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + int check = 1; + while (check) + { + const GLenum err = glGetError(); + switch (err) + { + case GL_NO_ERROR: check = 0; break; + case 0x0500: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_ENUM"); break; + case 0x0501: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_VALUE"); break; + case 0x0502: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_OPERATION"); break; + case 0x0503: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_STACK_OVERFLOW"); break; + case 0x0504: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_STACK_UNDERFLOW"); break; + case 0x0505: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_OUT_OF_MEMORY"); break; + case 0x0506: TRACELOG(RL_LOG_WARNING, "GL: Error detected: GL_INVALID_FRAMEBUFFER_OPERATION"); break; + default: TRACELOG(RL_LOG_WARNING, "GL: Error detected: Unknown error code: %x", err); break; + } + } +#endif +} + +// Set blend mode +void rlSetBlendMode(int mode) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.State.currentBlendMode != mode) || ((mode == RL_BLEND_CUSTOM || mode == RL_BLEND_CUSTOM_SEPARATE) && RLGL.State.glCustomBlendModeModified)) + { + rlDrawRenderBatch(RLGL.currentBatch); + + switch (mode) + { + case RL_BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_ADD_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_SUBTRACT_COLORS: glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_SUBTRACT); break; + case RL_BLEND_ALPHA_PREMULTIPLY: glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); break; + case RL_BLEND_CUSTOM: + { + // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactors() + glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation); + + } break; + case RL_BLEND_CUSTOM_SEPARATE: + { + // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactorsSeparate() + glBlendFuncSeparate(RLGL.State.glBlendSrcFactorRGB, RLGL.State.glBlendDestFactorRGB, RLGL.State.glBlendSrcFactorAlpha, RLGL.State.glBlendDestFactorAlpha); + glBlendEquationSeparate(RLGL.State.glBlendEquationRGB, RLGL.State.glBlendEquationAlpha); + + } break; + default: break; + } + + RLGL.State.currentBlendMode = mode; + RLGL.State.glCustomBlendModeModified = false; + } +#endif +} + +// Set blending mode factor and equation +void rlSetBlendFactors(int glSrcFactor, int glDstFactor, int glEquation) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.State.glBlendSrcFactor != glSrcFactor) || + (RLGL.State.glBlendDstFactor != glDstFactor) || + (RLGL.State.glBlendEquation != glEquation)) + { + RLGL.State.glBlendSrcFactor = glSrcFactor; + RLGL.State.glBlendDstFactor = glDstFactor; + RLGL.State.glBlendEquation = glEquation; + + RLGL.State.glCustomBlendModeModified = true; + } +#endif +} + +// Set blending mode factor and equation separately for RGB and alpha +void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int glDstAlpha, int glEqRGB, int glEqAlpha) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.State.glBlendSrcFactorRGB != glSrcRGB) || + (RLGL.State.glBlendDestFactorRGB != glDstRGB) || + (RLGL.State.glBlendSrcFactorAlpha != glSrcAlpha) || + (RLGL.State.glBlendDestFactorAlpha != glDstAlpha) || + (RLGL.State.glBlendEquationRGB != glEqRGB) || + (RLGL.State.glBlendEquationAlpha != glEqAlpha)) + { + RLGL.State.glBlendSrcFactorRGB = glSrcRGB; + RLGL.State.glBlendDestFactorRGB = glDstRGB; + RLGL.State.glBlendSrcFactorAlpha = glSrcAlpha; + RLGL.State.glBlendDestFactorAlpha = glDstAlpha; + RLGL.State.glBlendEquationRGB = glEqRGB; + RLGL.State.glBlendEquationAlpha = glEqAlpha; + + RLGL.State.glCustomBlendModeModified = true; + } +#endif +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - OpenGL Debug +//---------------------------------------------------------------------------------- +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43) +static void GLAPIENTRY rlDebugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) +{ + // Ignore non-significant error/warning codes (NVidia drivers) + // NOTE: Here there are the details with a sample output: + // - #131169 - Framebuffer detailed info: The driver allocated storage for renderbuffer 2. (severity: low) + // - #131185 - Buffer detailed info: Buffer object 1 (bound to GL_ELEMENT_ARRAY_BUFFER_ARB, usage hint is GL_ENUM_88e4) + // will use VIDEO memory as the source for buffer object operations. (severity: low) + // - #131218 - Program/shader state performance warning: Vertex shader in program 7 is being recompiled based on GL state. (severity: medium) + // - #131204 - Texture state usage warning: The texture object (0) bound to texture image unit 0 does not have + // a defined base level and cannot be used for texture mapping. (severity: low) + if ((id == 131169) || (id == 131185) || (id == 131218) || (id == 131204)) return; + + const char *msgSource = NULL; + switch (source) + { + case GL_DEBUG_SOURCE_API: msgSource = "API"; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: msgSource = "WINDOW_SYSTEM"; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: msgSource = "SHADER_COMPILER"; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: msgSource = "THIRD_PARTY"; break; + case GL_DEBUG_SOURCE_APPLICATION: msgSource = "APPLICATION"; break; + case GL_DEBUG_SOURCE_OTHER: msgSource = "OTHER"; break; + default: break; + } + + const char *msgType = NULL; + switch (type) + { + case GL_DEBUG_TYPE_ERROR: msgType = "ERROR"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: msgType = "DEPRECATED_BEHAVIOR"; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: msgType = "UNDEFINED_BEHAVIOR"; break; + case GL_DEBUG_TYPE_PORTABILITY: msgType = "PORTABILITY"; break; + case GL_DEBUG_TYPE_PERFORMANCE: msgType = "PERFORMANCE"; break; + case GL_DEBUG_TYPE_MARKER: msgType = "MARKER"; break; + case GL_DEBUG_TYPE_PUSH_GROUP: msgType = "PUSH_GROUP"; break; + case GL_DEBUG_TYPE_POP_GROUP: msgType = "POP_GROUP"; break; + case GL_DEBUG_TYPE_OTHER: msgType = "OTHER"; break; + default: break; + } + + const char *msgSeverity = "DEFAULT"; + switch (severity) + { + case GL_DEBUG_SEVERITY_LOW: msgSeverity = "LOW"; break; + case GL_DEBUG_SEVERITY_MEDIUM: msgSeverity = "MEDIUM"; break; + case GL_DEBUG_SEVERITY_HIGH: msgSeverity = "HIGH"; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: msgSeverity = "NOTIFICATION"; break; + default: break; + } + + TRACELOG(LOG_WARNING, "GL: OpenGL debug message: %s", message); + TRACELOG(LOG_WARNING, " > Type: %s", msgType); + TRACELOG(LOG_WARNING, " > Source = %s", msgSource); + TRACELOG(LOG_WARNING, " > Severity = %s", msgSeverity); +} +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition - rlgl functionality +//---------------------------------------------------------------------------------- + +// Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states +void rlglInit(int width, int height) +{ + // Enable OpenGL debug context if required +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43) + if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL)) + { + glDebugMessageCallback(rlDebugMessageCallback, 0); + // glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_HIGH, 0, 0, GL_TRUE); // TODO: Filter message + + // Debug context options: + // - GL_DEBUG_OUTPUT - Faster version but not useful for breakpoints + // - GL_DEBUG_OUTPUT_SYNCHRONUS - Callback is in sync with errors, so a breakpoint can be placed on the callback in order to get a stacktrace for the GL error + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + } +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Init default white texture + unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) + RLGL.State.defaultTextureId = rlLoadTexture(pixels, 1, 1, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1); + + if (RLGL.State.defaultTextureId != 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture loaded successfully", RLGL.State.defaultTextureId); + else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load default texture"); + + // Init default Shader (customized for GL 3.3 and ES2) + // Loaded: RLGL.State.defaultShaderId + RLGL.State.defaultShaderLocs + rlLoadShaderDefault(); + RLGL.State.currentShaderId = RLGL.State.defaultShaderId; + RLGL.State.currentShaderLocs = RLGL.State.defaultShaderLocs; + + // Init default vertex arrays buffers + RLGL.defaultBatch = rlLoadRenderBatch(RL_DEFAULT_BATCH_BUFFERS, RL_DEFAULT_BATCH_BUFFER_ELEMENTS); + RLGL.currentBatch = &RLGL.defaultBatch; + + // Init stack matrices (emulating OpenGL 1.1) + for (int i = 0; i < RL_MAX_MATRIX_STACK_SIZE; i++) RLGL.State.stack[i] = rlMatrixIdentity(); + + // Init internal matrices + RLGL.State.transform = rlMatrixIdentity(); + RLGL.State.projection = rlMatrixIdentity(); + RLGL.State.modelview = rlMatrixIdentity(); + RLGL.State.currentMatrix = &RLGL.State.modelview; +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + + // Initialize OpenGL default states + //---------------------------------------------------------- + // Init state: Depth test + glDepthFunc(GL_LEQUAL); // Type of depth testing to apply + glDisable(GL_DEPTH_TEST); // Disable depth testing for 2D (only used for 3D) + + // Init state: Blending mode + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed) + glEnable(GL_BLEND); // Enable color blending (required to work with transparencies) + + // Init state: Culling + // NOTE: All shapes/models triangles are drawn CCW + glCullFace(GL_BACK); // Cull the back face (default) + glFrontFace(GL_CCW); // Front face are defined counter clockwise (default) + glEnable(GL_CULL_FACE); // Enable backface culling + + // Init state: Cubemap seamless +#if defined(GRAPHICS_API_OPENGL_33) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Seamless cubemaps (not supported on OpenGL ES 2.0) +#endif + +#if defined(GRAPHICS_API_OPENGL_11) + // Init state: Color hints (deprecated in OpenGL 3.0+) + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation + glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) +#endif + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Store screen size into global variables + RLGL.State.framebufferWidth = width; + RLGL.State.framebufferHeight = height; + + TRACELOG(RL_LOG_INFO, "RLGL: Default OpenGL state initialized successfully"); + //---------------------------------------------------------- +#endif + + // Init state: Color/Depth buffers clear + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black) + glClearDepth(1.0f); // Set clear depth value (default) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers (depth buffer required for 3D) +} + +// Vertex Buffer Object deinitialization (memory free) +void rlglClose(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlUnloadRenderBatch(RLGL.defaultBatch); + + rlUnloadShaderDefault(); // Unload default shader + + glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture + TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId); +#endif +} + +// Load OpenGL extensions +// NOTE: External loader function must be provided +void rlLoadExtensions(void *loader) +{ +#if defined(GRAPHICS_API_OPENGL_33) // Also defined for GRAPHICS_API_OPENGL_21 + // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions (and lower versions) + if (gladLoadGL((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); + else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); + + // Get number of supported extensions + GLint numExt = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); + TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt); + +#if defined(RLGL_SHOW_GL_DETAILS_INFO) + // Get supported extensions list + // WARNING: glGetStringi() not available on OpenGL 2.1 + TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:"); + for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", glGetStringi(GL_EXTENSIONS, i)); +#endif + +#if defined(GRAPHICS_API_OPENGL_21) + // Register supported extensions flags + // Optional OpenGL 2.1 extensions + RLGL.ExtSupported.vao = GLAD_GL_ARB_vertex_array_object; + RLGL.ExtSupported.instancing = (GLAD_GL_EXT_draw_instanced && GLAD_GL_ARB_instanced_arrays); + RLGL.ExtSupported.texNPOT = GLAD_GL_ARB_texture_non_power_of_two; + RLGL.ExtSupported.texFloat32 = GLAD_GL_ARB_texture_float; + RLGL.ExtSupported.texDepth = GLAD_GL_ARB_depth_texture; + RLGL.ExtSupported.maxDepthBits = 32; + RLGL.ExtSupported.texAnisoFilter = GLAD_GL_EXT_texture_filter_anisotropic; + RLGL.ExtSupported.texMirrorClamp = GLAD_GL_EXT_texture_mirror_clamp; +#else + // Register supported extensions flags + // OpenGL 3.3 extensions supported by default (core) + RLGL.ExtSupported.vao = true; + RLGL.ExtSupported.instancing = true; + RLGL.ExtSupported.texNPOT = true; + RLGL.ExtSupported.texFloat32 = true; + RLGL.ExtSupported.texDepth = true; + RLGL.ExtSupported.maxDepthBits = 32; + RLGL.ExtSupported.texAnisoFilter = true; + RLGL.ExtSupported.texMirrorClamp = true; +#endif + + // Optional OpenGL 3.3 extensions + RLGL.ExtSupported.texCompASTC = GLAD_GL_KHR_texture_compression_astc_hdr && GLAD_GL_KHR_texture_compression_astc_ldr; + RLGL.ExtSupported.texCompDXT = GLAD_GL_EXT_texture_compression_s3tc; // Texture compression: DXT + RLGL.ExtSupported.texCompETC2 = GLAD_GL_ARB_ES3_compatibility; // Texture compression: ETC2/EAC + #if defined(GRAPHICS_API_OPENGL_43) + RLGL.ExtSupported.computeShader = GLAD_GL_ARB_compute_shader; + RLGL.ExtSupported.ssbo = GLAD_GL_ARB_shader_storage_buffer_object; + #endif + +#endif // GRAPHICS_API_OPENGL_33 + +#if defined(GRAPHICS_API_OPENGL_ES2) + + #if defined(PLATFORM_DESKTOP) + if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); + else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES2.0 loaded successfully"); + #endif + + // Get supported extensions list + GLint numExt = 0; + const char **extList = RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) + const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string + + // NOTE: We have to duplicate string because glGetString() returns a const string + int size = strlen(extensions) + 1; // Get extensions string size in bytes + char *extensionsDup = (char *)RL_CALLOC(size, sizeof(char)); + strcpy(extensionsDup, extensions); + extList[numExt] = extensionsDup; + + for (int i = 0; i < size; i++) + { + if (extensionsDup[i] == ' ') + { + extensionsDup[i] = '\0'; + numExt++; + extList[numExt] = &extensionsDup[i + 1]; + } + } + + TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt); + +#if defined(RLGL_SHOW_GL_DETAILS_INFO) + TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:"); + for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", extList[i]); +#endif + + // Check required extensions + for (int i = 0; i < numExt; i++) + { + // Check VAO support + // NOTE: Only check on OpenGL ES, OpenGL 3.3 has VAO support as core feature + if (strcmp(extList[i], (const char *)"GL_OES_vertex_array_object") == 0) + { + // The extension is supported by our hardware and driver, try to get related functions pointers + // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... + glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)((rlglLoadProc)loader)("glGenVertexArraysOES"); + glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)((rlglLoadProc)loader)("glBindVertexArrayOES"); + glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)((rlglLoadProc)loader)("glDeleteVertexArraysOES"); + //glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)loader("glIsVertexArrayOES"); // NOTE: Fails in WebGL, omitted + + if ((glGenVertexArrays != NULL) && (glBindVertexArray != NULL) && (glDeleteVertexArrays != NULL)) RLGL.ExtSupported.vao = true; + } + + // Check instanced rendering support + if (strcmp(extList[i], (const char *)"GL_ANGLE_instanced_arrays") == 0) // Web ANGLE + { + glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedANGLE"); + glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedANGLE"); + glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISOREXTPROC)((rlglLoadProc)loader)("glVertexAttribDivisorANGLE"); + + if ((glDrawArraysInstanced != NULL) && (glDrawElementsInstanced != NULL) && (glVertexAttribDivisor != NULL)) RLGL.ExtSupported.instancing = true; + } + else + { + if ((strcmp(extList[i], (const char *)"GL_EXT_draw_instanced") == 0) && // Standard EXT + (strcmp(extList[i], (const char *)"GL_EXT_instanced_arrays") == 0)) + { + glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedEXT"); + glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedEXT"); + glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISOREXTPROC)((rlglLoadProc)loader)("glVertexAttribDivisorEXT"); + + if ((glDrawArraysInstanced != NULL) && (glDrawElementsInstanced != NULL) && (glVertexAttribDivisor != NULL)) RLGL.ExtSupported.instancing = true; + } + } + + // Check NPOT textures support + // NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature + if (strcmp(extList[i], (const char *)"GL_OES_texture_npot") == 0) RLGL.ExtSupported.texNPOT = true; + + // Check texture float support + if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) RLGL.ExtSupported.texFloat32 = true; + + // Check depth texture support + if (strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) RLGL.ExtSupported.texDepth = true; + if (strcmp(extList[i], (const char *)"GL_WEBGL_depth_texture") == 0) RLGL.ExtSupported.texDepthWebGL = true; // WebGL requires unsized internal format + if (RLGL.ExtSupported.texDepthWebGL) RLGL.ExtSupported.texDepth = true; + + if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) RLGL.ExtSupported.maxDepthBits = 24; // Not available on WebGL + if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) RLGL.ExtSupported.maxDepthBits = 32; // Not available on WebGL + + // Check texture compression support: DXT + if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_s3tc") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBKIT_WEBGL_compressed_texture_s3tc") == 0)) RLGL.ExtSupported.texCompDXT = true; + + // Check texture compression support: ETC1 + if ((strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) || + (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_etc1") == 0)) RLGL.ExtSupported.texCompETC1 = true; + + // Check texture compression support: ETC2/EAC + if (strcmp(extList[i], (const char *)"GL_ARB_ES3_compatibility") == 0) RLGL.ExtSupported.texCompETC2 = true; + + // Check texture compression support: PVR + if (strcmp(extList[i], (const char *)"GL_IMG_texture_compression_pvrtc") == 0) RLGL.ExtSupported.texCompPVRT = true; + + // Check texture compression support: ASTC + if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) RLGL.ExtSupported.texCompASTC = true; + + // Check anisotropic texture filter support + if (strcmp(extList[i], (const char *)"GL_EXT_texture_filter_anisotropic") == 0) RLGL.ExtSupported.texAnisoFilter = true; + + // Check clamp mirror wrap mode support + if (strcmp(extList[i], (const char *)"GL_EXT_texture_mirror_clamp") == 0) RLGL.ExtSupported.texMirrorClamp = true; + } + + // Free extensions pointers + RL_FREE(extList); + RL_FREE(extensionsDup); // Duplicated string must be deallocated +#endif // GRAPHICS_API_OPENGL_ES2 + + // Check OpenGL information and capabilities + //------------------------------------------------------------------------------ + // Show current OpenGL and GLSL version + TRACELOG(RL_LOG_INFO, "GL: OpenGL device information:"); + TRACELOG(RL_LOG_INFO, " > Vendor: %s", glGetString(GL_VENDOR)); + TRACELOG(RL_LOG_INFO, " > Renderer: %s", glGetString(GL_RENDERER)); + TRACELOG(RL_LOG_INFO, " > Version: %s", glGetString(GL_VERSION)); + TRACELOG(RL_LOG_INFO, " > GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: Anisotropy levels capability is an extension + #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + #endif + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &RLGL.ExtSupported.maxAnisotropyLevel); + +#if defined(RLGL_SHOW_GL_DETAILS_INFO) + // Show some OpenGL GPU capabilities + TRACELOG(RL_LOG_INFO, "GL: OpenGL capabilities:"); + GLint capability = 0; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_TEXTURE_SIZE: %i", capability); + glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_CUBE_MAP_TEXTURE_SIZE: %i", capability); + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_TEXTURE_IMAGE_UNITS: %i", capability); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_VERTEX_ATTRIBS: %i", capability); + #if !defined(GRAPHICS_API_OPENGL_ES2) + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_UNIFORM_BLOCK_SIZE: %i", capability); + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_DRAW_BUFFERS: %i", capability); + if (RLGL.ExtSupported.texAnisoFilter) TRACELOG(RL_LOG_INFO, " GL_MAX_TEXTURE_MAX_ANISOTROPY: %.0f", RLGL.ExtSupported.maxAnisotropyLevel); + #endif + glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &capability); + TRACELOG(RL_LOG_INFO, " GL_NUM_COMPRESSED_TEXTURE_FORMATS: %i", capability); + GLint *compFormats = (GLint *)RL_CALLOC(capability, sizeof(GLint)); + glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, compFormats); + for (int i = 0; i < capability; i++) TRACELOG(RL_LOG_INFO, " %s", rlGetCompressedFormatName(compFormats[i])); + RL_FREE(compFormats); + +#if defined(GRAPHICS_API_OPENGL_43) + glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_VERTEX_ATTRIB_BINDINGS: %i", capability); + glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &capability); + TRACELOG(RL_LOG_INFO, " GL_MAX_UNIFORM_LOCATIONS: %i", capability); +#endif // GRAPHICS_API_OPENGL_43 +#else // RLGL_SHOW_GL_DETAILS_INFO + + // Show some basic info about GL supported features + if (RLGL.ExtSupported.vao) TRACELOG(RL_LOG_INFO, "GL: VAO extension detected, VAO functions loaded successfully"); + else TRACELOG(RL_LOG_WARNING, "GL: VAO extension not found, VAO not supported"); + if (RLGL.ExtSupported.texNPOT) TRACELOG(RL_LOG_INFO, "GL: NPOT textures extension detected, full NPOT textures supported"); + else TRACELOG(RL_LOG_WARNING, "GL: NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); + if (RLGL.ExtSupported.texCompDXT) TRACELOG(RL_LOG_INFO, "GL: DXT compressed textures supported"); + if (RLGL.ExtSupported.texCompETC1) TRACELOG(RL_LOG_INFO, "GL: ETC1 compressed textures supported"); + if (RLGL.ExtSupported.texCompETC2) TRACELOG(RL_LOG_INFO, "GL: ETC2/EAC compressed textures supported"); + if (RLGL.ExtSupported.texCompPVRT) TRACELOG(RL_LOG_INFO, "GL: PVRT compressed textures supported"); + if (RLGL.ExtSupported.texCompASTC) TRACELOG(RL_LOG_INFO, "GL: ASTC compressed textures supported"); + if (RLGL.ExtSupported.computeShader) TRACELOG(RL_LOG_INFO, "GL: Compute shaders supported"); + if (RLGL.ExtSupported.ssbo) TRACELOG(RL_LOG_INFO, "GL: Shader storage buffer objects supported"); +#endif // RLGL_SHOW_GL_DETAILS_INFO + +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 +} + +// Get current OpenGL version +int rlGetVersion(void) +{ + int glVersion = 0; +#if defined(GRAPHICS_API_OPENGL_11) + glVersion = RL_OPENGL_11; +#endif +#if defined(GRAPHICS_API_OPENGL_21) + glVersion = RL_OPENGL_21; +#elif defined(GRAPHICS_API_OPENGL_33) + glVersion = RL_OPENGL_33; +#endif +#if defined(GRAPHICS_API_OPENGL_43) + glVersion = RL_OPENGL_43; +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + glVersion = RL_OPENGL_ES_20; +#endif + return glVersion; +} + +// Set current framebuffer width +void rlSetFramebufferWidth(int width) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.framebufferWidth = width; +#endif +} + +// Set current framebuffer height +void rlSetFramebufferHeight(int height) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.framebufferHeight = height; +#endif +} + +// Get default framebuffer width +int rlGetFramebufferWidth(void) +{ + int width = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + width = RLGL.State.framebufferWidth; +#endif + return width; +} + +// Get default framebuffer height +int rlGetFramebufferHeight(void) +{ + int height = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + height = RLGL.State.framebufferHeight; +#endif + return height; +} + +// Get default internal texture (white texture) +// NOTE: Default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 +unsigned int rlGetTextureIdDefault(void) +{ + unsigned int id = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + id = RLGL.State.defaultTextureId; +#endif + return id; +} + +// Get default shader id +unsigned int rlGetShaderIdDefault(void) +{ + unsigned int id = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + id = RLGL.State.defaultShaderId; +#endif + return id; +} + +// Get default shader locs +int *rlGetShaderLocsDefault(void) +{ + int *locs = NULL; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + locs = RLGL.State.defaultShaderLocs; +#endif + return locs; +} + +// Render batch management +//------------------------------------------------------------------------------------------------ +// Load render batch +rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements) +{ + rlRenderBatch batch = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Initialize CPU (RAM) vertex buffers (position, texcoord, color data and indexes) + //-------------------------------------------------------------------------------------------- + batch.vertexBuffer = (rlVertexBuffer *)RL_MALLOC(numBuffers*sizeof(rlVertexBuffer)); + + for (int i = 0; i < numBuffers; i++) + { + batch.vertexBuffer[i].elementCount = bufferElements; + + batch.vertexBuffer[i].vertices = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float)); // 3 float by vertex, 4 vertex by quad + batch.vertexBuffer[i].texcoords = (float *)RL_MALLOC(bufferElements*2*4*sizeof(float)); // 2 float by texcoord, 4 texcoord by quad + batch.vertexBuffer[i].colors = (unsigned char *)RL_MALLOC(bufferElements*4*4*sizeof(unsigned char)); // 4 float by color, 4 colors by quad +#if defined(GRAPHICS_API_OPENGL_33) + batch.vertexBuffer[i].indices = (unsigned int *)RL_MALLOC(bufferElements*6*sizeof(unsigned int)); // 6 int by quad (indices) +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + batch.vertexBuffer[i].indices = (unsigned short *)RL_MALLOC(bufferElements*6*sizeof(unsigned short)); // 6 int by quad (indices) +#endif + + for (int j = 0; j < (3*4*bufferElements); j++) batch.vertexBuffer[i].vertices[j] = 0.0f; + for (int j = 0; j < (2*4*bufferElements); j++) batch.vertexBuffer[i].texcoords[j] = 0.0f; + for (int j = 0; j < (4*4*bufferElements); j++) batch.vertexBuffer[i].colors[j] = 0; + + int k = 0; + + // Indices can be initialized right now + for (int j = 0; j < (6*bufferElements); j += 6) + { + batch.vertexBuffer[i].indices[j] = 4*k; + batch.vertexBuffer[i].indices[j + 1] = 4*k + 1; + batch.vertexBuffer[i].indices[j + 2] = 4*k + 2; + batch.vertexBuffer[i].indices[j + 3] = 4*k; + batch.vertexBuffer[i].indices[j + 4] = 4*k + 2; + batch.vertexBuffer[i].indices[j + 5] = 4*k + 3; + + k++; + } + + RLGL.State.vertexCounter = 0; + } + + TRACELOG(RL_LOG_INFO, "RLGL: Render batch vertex buffers loaded successfully in RAM (CPU)"); + //-------------------------------------------------------------------------------------------- + + // Upload to GPU (VRAM) vertex data and initialize VAOs/VBOs + //-------------------------------------------------------------------------------------------- + for (int i = 0; i < numBuffers; i++) + { + if (RLGL.ExtSupported.vao) + { + // Initialize Quads VAO + glGenVertexArrays(1, &batch.vertexBuffer[i].vaoId); + glBindVertexArray(batch.vertexBuffer[i].vaoId); + } + + // Quads - Vertex buffers binding and attributes enable + // Vertex position buffer (shader-location = 0) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[0]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*3*4*sizeof(float), batch.vertexBuffer[i].vertices, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + + // Vertex texcoord buffer (shader-location = 1) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[1]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*2*4*sizeof(float), batch.vertexBuffer[i].texcoords, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + + // Vertex color buffer (shader-location = 3) + glGenBuffers(1, &batch.vertexBuffer[i].vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[2]); + glBufferData(GL_ARRAY_BUFFER, bufferElements*4*4*sizeof(unsigned char), batch.vertexBuffer[i].colors, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + + // Fill index buffer + glGenBuffers(1, &batch.vertexBuffer[i].vboId[3]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.vertexBuffer[i].vboId[3]); +#if defined(GRAPHICS_API_OPENGL_33) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(int), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + glBufferData(GL_ELEMENT_ARRAY_BUFFER, bufferElements*6*sizeof(short), batch.vertexBuffer[i].indices, GL_STATIC_DRAW); +#endif + } + + TRACELOG(RL_LOG_INFO, "RLGL: Render batch vertex buffers loaded successfully in VRAM (GPU)"); + + // Unbind the current VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(0); + //-------------------------------------------------------------------------------------------- + + // Init draw calls tracking system + //-------------------------------------------------------------------------------------------- + batch.draws = (rlDrawCall *)RL_MALLOC(RL_DEFAULT_BATCH_DRAWCALLS*sizeof(rlDrawCall)); + + for (int i = 0; i < RL_DEFAULT_BATCH_DRAWCALLS; i++) + { + batch.draws[i].mode = RL_QUADS; + batch.draws[i].vertexCount = 0; + batch.draws[i].vertexAlignment = 0; + //batch.draws[i].vaoId = 0; + //batch.draws[i].shaderId = 0; + batch.draws[i].textureId = RLGL.State.defaultTextureId; + //batch.draws[i].RLGL.State.projection = rlMatrixIdentity(); + //batch.draws[i].RLGL.State.modelview = rlMatrixIdentity(); + } + + batch.bufferCount = numBuffers; // Record buffer count + batch.drawCounter = 1; // Reset draws counter + batch.currentDepth = -1.0f; // Reset depth value + //-------------------------------------------------------------------------------------------- +#endif + + return batch; +} + +// Unload default internal buffers vertex data from CPU and GPU +void rlUnloadRenderBatch(rlRenderBatch batch) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Unbind everything + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // Unload all vertex buffers data + for (int i = 0; i < batch.bufferCount; i++) + { + // Unbind VAO attribs data + if (RLGL.ExtSupported.vao) + { + glBindVertexArray(batch.vertexBuffer[i].vaoId); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + glBindVertexArray(0); + } + + // Delete VBOs from GPU (VRAM) + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[0]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[1]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[2]); + glDeleteBuffers(1, &batch.vertexBuffer[i].vboId[3]); + + // Delete VAOs from GPU (VRAM) + if (RLGL.ExtSupported.vao) glDeleteVertexArrays(1, &batch.vertexBuffer[i].vaoId); + + // Free vertex arrays memory from CPU (RAM) + RL_FREE(batch.vertexBuffer[i].vertices); + RL_FREE(batch.vertexBuffer[i].texcoords); + RL_FREE(batch.vertexBuffer[i].colors); + RL_FREE(batch.vertexBuffer[i].indices); + } + + // Unload arrays + RL_FREE(batch.vertexBuffer); + RL_FREE(batch.draws); +#endif +} + +// Draw render batch +// NOTE: We require a pointer to reset batch and increase current buffer (multi-buffer) +void rlDrawRenderBatch(rlRenderBatch *batch) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Update batch vertex buffers + //------------------------------------------------------------------------------------------------------------ + // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) + // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) + if (RLGL.State.vertexCounter > 0) + { + // Activate elements VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); + + // Vertex positions buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*3*sizeof(float), batch->vertexBuffer[batch->currentBuffer].vertices); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].vertices, GL_DYNAMIC_DRAW); // Update all buffer + + // Texture coordinates buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); + glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*2*sizeof(float), batch->vertexBuffer[batch->currentBuffer].texcoords); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].texcoords, GL_DYNAMIC_DRAW); // Update all buffer + + // Colors buffer + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); + glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*4*sizeof(unsigned char), batch->vertexBuffer[batch->currentBuffer].colors); + //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].colors, GL_DYNAMIC_DRAW); // Update all buffer + + // NOTE: glMapBuffer() causes sync issue. + // If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job. + // To avoid waiting (idle), you can call first glBufferData() with NULL pointer before glMapBuffer(). + // If you do that, the previous data in PBO will be discarded and glMapBuffer() returns a new + // allocated pointer immediately even if GPU is still working with the previous data. + + // Another option: map the buffer object into client's memory + // Probably this code could be moved somewhere else... + // batch->vertexBuffer[batch->currentBuffer].vertices = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); + // if (batch->vertexBuffer[batch->currentBuffer].vertices) + // { + // Update vertex data + // } + // glUnmapBuffer(GL_ARRAY_BUFFER); + + // Unbind the current VAO + if (RLGL.ExtSupported.vao) glBindVertexArray(0); + } + //------------------------------------------------------------------------------------------------------------ + + // Draw batch vertex buffers (considering VR stereo if required) + //------------------------------------------------------------------------------------------------------------ + Matrix matProjection = RLGL.State.projection; + Matrix matModelView = RLGL.State.modelview; + + int eyeCount = 1; + if (RLGL.State.stereoRender) eyeCount = 2; + + for (int eye = 0; eye < eyeCount; eye++) + { + if (eyeCount == 2) + { + // Setup current eye viewport (half screen width) + rlViewport(eye*RLGL.State.framebufferWidth/2, 0, RLGL.State.framebufferWidth/2, RLGL.State.framebufferHeight); + + // Set current eye view offset to modelview matrix + rlSetMatrixModelview(rlMatrixMultiply(matModelView, RLGL.State.viewOffsetStereo[eye])); + // Set current eye projection matrix + rlSetMatrixProjection(RLGL.State.projectionStereo[eye]); + } + + // Draw buffers + if (RLGL.State.vertexCounter > 0) + { + // Set current shader and upload current MVP matrix + glUseProgram(RLGL.State.currentShaderId); + + // Create modelview-projection matrix and upload to shader + Matrix matMVP = rlMatrixMultiply(RLGL.State.modelview, RLGL.State.projection); + float matMVPfloat[16] = { + matMVP.m0, matMVP.m1, matMVP.m2, matMVP.m3, + matMVP.m4, matMVP.m5, matMVP.m6, matMVP.m7, + matMVP.m8, matMVP.m9, matMVP.m10, matMVP.m11, + matMVP.m12, matMVP.m13, matMVP.m14, matMVP.m15 + }; + glUniformMatrix4fv(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_MVP], 1, false, matMVPfloat); + + if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId); + else + { + // Bind vertex attrib: position (shader-location = 0) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[0]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_POSITION]); + + // Bind vertex attrib: texcoord (shader-location = 1) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[1]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01]); + + // Bind vertex attrib: color (shader-location = 3) + glBindBuffer(GL_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[2]); + glVertexAttribPointer(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(RLGL.State.currentShaderLocs[RL_SHADER_LOC_VERTEX_COLOR]); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch->vertexBuffer[batch->currentBuffer].vboId[3]); + } + + // Setup some default shader values + glUniform4f(RLGL.State.currentShaderLocs[RL_SHADER_LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1i(RLGL.State.currentShaderLocs[RL_SHADER_LOC_MAP_DIFFUSE], 0); // Active default sampler2D: texture0 + + // Activate additional sampler textures + // Those additional textures will be common for all draw calls of the batch + for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) + { + if (RLGL.State.activeTextureId[i] > 0) + { + glActiveTexture(GL_TEXTURE0 + 1 + i); + glBindTexture(GL_TEXTURE_2D, RLGL.State.activeTextureId[i]); + } + } + + // Activate default sampler2D texture0 (one texture is always active for default batch shader) + // NOTE: Batch system accumulates calls by texture0 changes, additional textures are enabled for all the draw calls + glActiveTexture(GL_TEXTURE0); + + for (int i = 0, vertexOffset = 0; i < batch->drawCounter; i++) + { + // Bind current draw call texture, activated as GL_TEXTURE0 and Bound to sampler2D texture0 by default + glBindTexture(GL_TEXTURE_2D, batch->draws[i].textureId); + + if ((batch->draws[i].mode == RL_LINES) || (batch->draws[i].mode == RL_TRIANGLES)) glDrawArrays(batch->draws[i].mode, vertexOffset, batch->draws[i].vertexCount); + else + { +#if defined(GRAPHICS_API_OPENGL_33) + // We need to define the number of indices to be processed: elementCount*6 + // NOTE: The final parameter tells the GPU the offset in bytes from the + // start of the index buffer to the location of the first index to process + glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(vertexOffset/4*6*sizeof(GLuint))); +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(vertexOffset/4*6*sizeof(GLushort))); +#endif + } + + vertexOffset += (batch->draws[i].vertexCount + batch->draws[i].vertexAlignment); + } + + if (!RLGL.ExtSupported.vao) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + } + + if (RLGL.ExtSupported.vao) glBindVertexArray(0); // Unbind VAO + + glUseProgram(0); // Unbind shader program + } + + // Restore viewport to default measures + if (eyeCount == 2) rlViewport(0, 0, RLGL.State.framebufferWidth, RLGL.State.framebufferHeight); + //------------------------------------------------------------------------------------------------------------ + + // Reset batch buffers + //------------------------------------------------------------------------------------------------------------ + // Reset vertex counter for next frame + RLGL.State.vertexCounter = 0; + + // Reset depth for next draw + batch->currentDepth = -1.0f; + + // Restore projection/modelview matrices + RLGL.State.projection = matProjection; + RLGL.State.modelview = matModelView; + + // Reset RLGL.currentBatch->draws array + for (int i = 0; i < RL_DEFAULT_BATCH_DRAWCALLS; i++) + { + batch->draws[i].mode = RL_QUADS; + batch->draws[i].vertexCount = 0; + batch->draws[i].textureId = RLGL.State.defaultTextureId; + } + + // Reset active texture units for next batch + for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) RLGL.State.activeTextureId[i] = 0; + + // Reset draws counter to one draw for the batch + batch->drawCounter = 1; + //------------------------------------------------------------------------------------------------------------ + + // Change to next buffer in the list (in case of multi-buffering) + batch->currentBuffer++; + if (batch->currentBuffer >= batch->bufferCount) batch->currentBuffer = 0; +#endif +} + +// Set the active render batch for rlgl +void rlSetRenderBatchActive(rlRenderBatch *batch) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlDrawRenderBatch(RLGL.currentBatch); + + if (batch != NULL) RLGL.currentBatch = batch; + else RLGL.currentBatch = &RLGL.defaultBatch; +#endif +} + +// Update and draw internal render batch +void rlDrawRenderBatchActive(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside +#endif +} + +// Check internal buffer overflow for a given number of vertex +// and force a rlRenderBatch draw call if required +bool rlCheckRenderBatchLimit(int vCount) +{ + bool overflow = false; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((RLGL.State.vertexCounter + vCount) >= + (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4)) + { + overflow = true; + + // Store current primitive drawing mode and texture id + int currentMode = RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode; + int currentTexture = RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId; + + rlDrawRenderBatch(RLGL.currentBatch); // NOTE: Stereo rendering is checked inside + + // Restore state of last batch so we can continue adding vertices + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = currentMode; + RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = currentTexture; + } +#endif + + return overflow; +} + +// Textures data management +//----------------------------------------------------------------------------------------- +// Convert image data to OpenGL texture (returns OpenGL valid Id) +unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount) +{ + unsigned int id = 0; + + glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding + + // Check texture format support by OpenGL 1.1 (compressed textures not supported) +#if defined(GRAPHICS_API_OPENGL_11) + if (format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) + { + TRACELOG(RL_LOG_WARNING, "GL: OpenGL 1.1 does not support GPU compressed texture formats"); + return id; + } +#else + if ((!RLGL.ExtSupported.texCompDXT) && ((format == RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) || (format == RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA) || + (format == RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA) || (format == RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA))) + { + TRACELOG(RL_LOG_WARNING, "GL: DXT compressed texture format not supported"); + return id; + } +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if ((!RLGL.ExtSupported.texCompETC1) && (format == RL_PIXELFORMAT_COMPRESSED_ETC1_RGB)) + { + TRACELOG(RL_LOG_WARNING, "GL: ETC1 compressed texture format not supported"); + return id; + } + + if ((!RLGL.ExtSupported.texCompETC2) && ((format == RL_PIXELFORMAT_COMPRESSED_ETC2_RGB) || (format == RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA))) + { + TRACELOG(RL_LOG_WARNING, "GL: ETC2 compressed texture format not supported"); + return id; + } + + if ((!RLGL.ExtSupported.texCompPVRT) && ((format == RL_PIXELFORMAT_COMPRESSED_PVRT_RGB) || (format == RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA))) + { + TRACELOG(RL_LOG_WARNING, "GL: PVRT compressed texture format not supported"); + return id; + } + + if ((!RLGL.ExtSupported.texCompASTC) && ((format == RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA) || (format == RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA))) + { + TRACELOG(RL_LOG_WARNING, "GL: ASTC compressed texture format not supported"); + return id; + } +#endif +#endif // GRAPHICS_API_OPENGL_11 + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glGenTextures(1, &id); // Generate texture id + + glBindTexture(GL_TEXTURE_2D, id); + + int mipWidth = width; + int mipHeight = height; + int mipOffset = 0; // Mipmap data offset + + // Load the different mipmap levels + for (int i = 0; i < mipmapCount; i++) + { + unsigned int mipSize = rlGetPixelDataSize(mipWidth, mipHeight, format); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); + + if (glInternalFormat != -1) + { + if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, (unsigned char *)data + mipOffset); +#if !defined(GRAPHICS_API_OPENGL_11) + else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, (unsigned char *)data + mipOffset); +#endif + +#if defined(GRAPHICS_API_OPENGL_33) + if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) + { + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + else if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) + { +#if defined(GRAPHICS_API_OPENGL_21) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; +#elif defined(GRAPHICS_API_OPENGL_33) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; +#endif + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } +#endif + } + + mipWidth /= 2; + mipHeight /= 2; + mipOffset += mipSize; + + // Security check for NPOT textures + if (mipWidth < 1) mipWidth = 1; + if (mipHeight < 1) mipHeight = 1; + } + + // Texture parameters configuration + // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used +#if defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used + if (RLGL.ExtSupported.texNPOT) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis + } + else + { + // NOTE: If using negative texture coordinates (LoadOBJ()), it does not work! + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set texture to clamp on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Set texture to clamp on y-axis + } +#else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repeat on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repeat on y-axis +#endif + + // Magnification and minification filters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Alternative: GL_LINEAR + +#if defined(GRAPHICS_API_OPENGL_33) + if (mipmapCount > 1) + { + // Activate Trilinear filtering if mipmaps are available + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } +#endif + + // At this point we have the texture loaded in GPU and texture parameters configured + + // NOTE: If mipmaps were not in data, they are not generated automatically + + // Unbind current texture + glBindTexture(GL_TEXTURE_2D, 0); + + if (id > 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Texture loaded successfully (%ix%i | %s | %i mipmaps)", id, width, height, rlGetPixelFormatName(format), mipmapCount); + else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load texture"); + + return id; +} + +// Load depth texture/renderbuffer (to be attached to fbo) +// WARNING: OpenGL ES 2.0 requires GL_OES_depth_texture and WebGL requires WEBGL_depth_texture extensions +unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // In case depth textures not supported, we force renderbuffer usage + if (!RLGL.ExtSupported.texDepth) useRenderBuffer = true; + + // NOTE: We let the implementation to choose the best bit-depth + // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F + unsigned int glInternalFormat = GL_DEPTH_COMPONENT; + +#if defined(GRAPHICS_API_OPENGL_ES2) + // WARNING: WebGL platform requires unsized internal format definition (GL_DEPTH_COMPONENT) + // while other platforms using OpenGL ES 2.0 require/support sized internal formats depending on the GPU capabilities + if (!RLGL.ExtSupported.texDepthWebGL || useRenderBuffer) + { + if (RLGL.ExtSupported.maxDepthBits == 32) glInternalFormat = GL_DEPTH_COMPONENT32_OES; + else if (RLGL.ExtSupported.maxDepthBits == 24) glInternalFormat = GL_DEPTH_COMPONENT24_OES; + else glInternalFormat = GL_DEPTH_COMPONENT16; + } +#endif + + if (!useRenderBuffer && RLGL.ExtSupported.texDepth) + { + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + glTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + + TRACELOG(RL_LOG_INFO, "TEXTURE: Depth texture loaded successfully"); + } + else + { + // Create the renderbuffer that will serve as the depth attachment for the framebuffer + // NOTE: A renderbuffer is simpler than a texture and could offer better performance on embedded devices + glGenRenderbuffers(1, &id); + glBindRenderbuffer(GL_RENDERBUFFER, id); + glRenderbufferStorage(GL_RENDERBUFFER, glInternalFormat, width, height); + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Depth renderbuffer loaded successfully (%i bits)", id, (RLGL.ExtSupported.maxDepthBits >= 24)? RLGL.ExtSupported.maxDepthBits : 16); + } +#endif + + return id; +} + +// Load texture cubemap +// NOTE: Cubemap data is expected to be 6 images in a single data array (one after the other), +// expected the following convention: +X, -X, +Y, -Y, +Z, -Z +unsigned int rlLoadTextureCubemap(const void *data, int size, int format) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int dataSize = rlGetPixelDataSize(size, size, format); + + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_CUBE_MAP, id); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + if (glInternalFormat != -1) + { + // Load cubemap faces + for (unsigned int i = 0; i < 6; i++) + { + if (data == NULL) + { + if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) + { + if (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32) + { + // Instead of using a sized internal texture format (GL_RGB16F, GL_RGB32F), we let the driver to choose the better format for us (GL_RGB) + if (RLGL.ExtSupported.texFloat32) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); + else TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); + } + else if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); + else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, NULL); + } + else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format"); + } + else + { + if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize); + else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, dataSize, (unsigned char *)data + i*dataSize); + } + +#if defined(GRAPHICS_API_OPENGL_33) + if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) + { + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } + else if (format == RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) + { +#if defined(GRAPHICS_API_OPENGL_21) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ALPHA }; +#elif defined(GRAPHICS_API_OPENGL_33) + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; +#endif + glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); + } +#endif + } + } + + // Set cubemap texture sampling parameters + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#if defined(GRAPHICS_API_OPENGL_33) + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Flag not supported on OpenGL ES 2.0 +#endif + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif + + if (id > 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Cubemap texture loaded successfully (%ix%i)", id, size, size); + else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load cubemap texture"); + + return id; +} + +// Update already loaded texture in GPU with new data +// NOTE: We don't know safely if internal texture format is the expected one... +void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data) +{ + glBindTexture(GL_TEXTURE_2D, id); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + + if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, data); + } + else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to update for current texture format (%i)", id, format); +} + +// Get OpenGL internal formats and data type from raylib PixelFormat +void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType) +{ + *glInternalFormat = 0; + *glFormat = 0; + *glType = 0; + + switch (format) + { + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + #if !defined(GRAPHICS_API_OPENGL_11) + case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + #endif + #elif defined(GRAPHICS_API_OPENGL_33) + case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + #endif + #if !defined(GRAPHICS_API_OPENGL_11) + case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: if (RLGL.ExtSupported.texCompETC1) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: if (RLGL.ExtSupported.texCompETC2) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: if (RLGL.ExtSupported.texCompPVRT) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU + case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: if (RLGL.ExtSupported.texCompASTC) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + #endif + default: TRACELOG(RL_LOG_WARNING, "TEXTURE: Current format not supported (%i)", format); break; + } +} + +// Unload texture from GPU memory +void rlUnloadTexture(unsigned int id) +{ + glDeleteTextures(1, &id); +} + +// Generate mipmap data for selected texture +// NOTE: Only supports GPU mipmap generation +void rlGenTextureMipmaps(unsigned int id, int width, int height, int format, int *mipmaps) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindTexture(GL_TEXTURE_2D, id); + + // Check if texture is power-of-two (POT) + bool texIsPOT = false; + + if (((width > 0) && ((width & (width - 1)) == 0)) && + ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true; + + if ((texIsPOT) || (RLGL.ExtSupported.texNPOT)) + { + //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorithm: GL_FASTEST, GL_NICEST, GL_DONT_CARE + glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically + + #define MIN(a,b) (((a)<(b))? (a):(b)) + #define MAX(a,b) (((a)>(b))? (a):(b)) + + *mipmaps = 1 + (int)floor(log(MAX(width, height))/log(2)); + TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Mipmaps generated automatically, total: %i", id, *mipmaps); + } + else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Failed to generate mipmaps", id); + + glBindTexture(GL_TEXTURE_2D, 0); +#else + TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] GPU mipmap generation not supported", id); +#endif +} + + +// Read texture pixel data +void *rlReadTexturePixels(unsigned int id, int width, int height, int format) +{ + void *pixels = NULL; + +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + glBindTexture(GL_TEXTURE_2D, id); + + // NOTE: Using texture id, we can retrieve some texture info (but not on OpenGL ES 2.0) + // Possible texture info: GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE + //int width, height, format; + //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); + + // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding. + // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting. + // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) + // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + unsigned int size = rlGetPixelDataSize(width, height, format); + + if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) + { + pixels = RL_MALLOC(size); + glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); + } + else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Data retrieval not suported for pixel format (%i)", id, format); + + glBindTexture(GL_TEXTURE_2D, 0); +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) + // glGetTexImage() is not available on OpenGL ES 2.0 + // Texture width and height are required on OpenGL ES 2.0. There is no way to get it from texture id. + // Two possible Options: + // 1 - Bind texture to color fbo attachment and glReadPixels() + // 2 - Create an fbo, activate it, render quad with texture, glReadPixels() + // We are using Option 1, just need to care for texture format on retrieval + // NOTE: This behaviour could be conditioned by graphic driver... + unsigned int fboId = rlLoadFramebuffer(width, height); + + glBindFramebuffer(GL_FRAMEBUFFER, fboId); + glBindTexture(GL_TEXTURE_2D, 0); + + // Attach our texture to FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0); + + // We read data as RGBA because FBO texture is configured as RGBA, despite binding another texture format + pixels = (unsigned char *)RL_MALLOC(rlGetPixelDataSize(width, height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Clean up temporal fbo + rlUnloadFramebuffer(fboId); +#endif + + return pixels; +} + +// Read screen pixel data (color buffer) +unsigned char *rlReadScreenPixels(int width, int height) +{ + unsigned char *screenData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char)); + + // NOTE 1: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer + // NOTE 2: We are getting alpha channel! Be careful, it can be transparent if not cleared properly! + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData); + + // Flip image vertically! + unsigned char *imgData = (unsigned char *)RL_MALLOC(width*height*4*sizeof(unsigned char)); + + for (int y = height - 1; y >= 0; y--) + { + for (int x = 0; x < (width*4); x++) + { + imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; // Flip line + + // Set alpha component value to 255 (no trasparent image retrieval) + // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it! + if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255; + } + } + + RL_FREE(screenData); + + return imgData; // NOTE: image data should be freed +} + +// Framebuffer management (fbo) +//----------------------------------------------------------------------------------------- +// Load a framebuffer to be used for rendering +// NOTE: No textures attached +unsigned int rlLoadFramebuffer(int width, int height) +{ + unsigned int fboId = 0; + +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glGenFramebuffers(1, &fboId); // Create the framebuffer object + glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer +#endif + + return fboId; +} + +// Attach color buffer texture to an fbo (unloads previous attachment) +// NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture +void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, fboId); + + switch (attachType) + { + case RL_ATTACHMENT_COLOR_CHANNEL0: + case RL_ATTACHMENT_COLOR_CHANNEL1: + case RL_ATTACHMENT_COLOR_CHANNEL2: + case RL_ATTACHMENT_COLOR_CHANNEL3: + case RL_ATTACHMENT_COLOR_CHANNEL4: + case RL_ATTACHMENT_COLOR_CHANNEL5: + case RL_ATTACHMENT_COLOR_CHANNEL6: + case RL_ATTACHMENT_COLOR_CHANNEL7: + { + if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, texId, mipLevel); + else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_RENDERBUFFER, texId); + else if (texType >= RL_ATTACHMENT_CUBEMAP_POSITIVE_X) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_CUBE_MAP_POSITIVE_X + texType, texId, mipLevel); + + } break; + case RL_ATTACHMENT_DEPTH: + { + if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); + else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, texId); + + } break; + case RL_ATTACHMENT_STENCIL: + { + if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); + else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, texId); + + } break; + default: break; + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif +} + +// Verify render texture is complete +bool rlFramebufferComplete(unsigned int id) +{ + bool result = false; + +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBindFramebuffer(GL_FRAMEBUFFER, id); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + switch (status) + { + case GL_FRAMEBUFFER_UNSUPPORTED: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer is unsupported", id); break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete attachment", id); break; +#if defined(GRAPHICS_API_OPENGL_ES2) + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer has incomplete dimensions", id); break; +#endif + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TRACELOG(RL_LOG_WARNING, "FBO: [ID %i] Framebuffer has a missing attachment", id); break; + default: break; + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + result = (status == GL_FRAMEBUFFER_COMPLETE); +#endif + + return result; +} + +// Unload framebuffer from GPU memory +// NOTE: All attached textures/cubemaps/renderbuffers are also deleted +void rlUnloadFramebuffer(unsigned int id) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) + + // Query depth attachment to automatically delete texture/renderbuffer + int depthType = 0, depthId = 0; + glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId); + + unsigned int depthIdU = (unsigned int)depthId; + if (depthType == GL_RENDERBUFFER) glDeleteRenderbuffers(1, &depthIdU); + else if (depthType == GL_TEXTURE) glDeleteTextures(1, &depthIdU); + + // NOTE: If a texture object is deleted while its image is attached to the *currently bound* framebuffer, + // the texture image is automatically detached from the currently bound framebuffer. + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &id); + + TRACELOG(RL_LOG_INFO, "FBO: [ID %i] Unloaded framebuffer from VRAM (GPU)", id); +#endif +} + +// Vertex data management +//----------------------------------------------------------------------------------------- +// Load a new attributes buffer +unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glGenBuffers(1, &id); + glBindBuffer(GL_ARRAY_BUFFER, id); + glBufferData(GL_ARRAY_BUFFER, size, buffer, dynamic? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); +#endif + + return id; +} + +// Load a new attributes element buffer +unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glGenBuffers(1, &id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, buffer, dynamic? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); +#endif + + return id; +} + +// Enable vertex buffer (VBO) +void rlEnableVertexBuffer(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ARRAY_BUFFER, id); +#endif +} + +// Disable vertex buffer (VBO) +void rlDisableVertexBuffer(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ARRAY_BUFFER, 0); +#endif +} + +// Enable vertex buffer element (VBO element) +void rlEnableVertexBufferElement(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); +#endif +} + +// Disable vertex buffer element (VBO element) +void rlDisableVertexBufferElement(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#endif +} + +// Update vertex buffer with new data +// NOTE: dataSize and offset must be provided in bytes +void rlUpdateVertexBuffer(unsigned int id, const void *data, int dataSize, int offset) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ARRAY_BUFFER, id); + glBufferSubData(GL_ARRAY_BUFFER, offset, dataSize, data); +#endif +} + +// Update vertex buffer elements with new data +// NOTE: dataSize and offset must be provided in bytes +void rlUpdateVertexBufferElements(unsigned int id, const void *data, int dataSize, int offset) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, id); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, dataSize, data); +#endif +} + +// Enable vertex array object (VAO) +bool rlEnableVertexArray(unsigned int vaoId) +{ + bool result = false; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.vao) + { + glBindVertexArray(vaoId); + result = true; + } +#endif + return result; +} + +// Disable vertex array object (VAO) +void rlDisableVertexArray(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.vao) glBindVertexArray(0); +#endif +} + +// Enable vertex attribute index +void rlEnableVertexAttribute(unsigned int index) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glEnableVertexAttribArray(index); +#endif +} + +// Disable vertex attribute index +void rlDisableVertexAttribute(unsigned int index) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDisableVertexAttribArray(index); +#endif +} + +// Draw vertex array +void rlDrawVertexArray(int offset, int count) +{ + glDrawArrays(GL_TRIANGLES, offset, count); +} + +// Draw vertex array elements +void rlDrawVertexArrayElements(int offset, int count, const void *buffer) +{ + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset); +} + +// Draw vertex array instanced +void rlDrawVertexArrayInstanced(int offset, int count, int instances) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDrawArraysInstanced(GL_TRIANGLES, 0, count, instances); +#endif +} + +// Draw vertex array elements instanced +void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset, instances); +#endif +} + +#if defined(GRAPHICS_API_OPENGL_11) +// Enable vertex state pointer +void rlEnableStatePointer(int vertexAttribType, void *buffer) +{ + if (buffer != NULL) glEnableClientState(vertexAttribType); + switch (vertexAttribType) + { + case GL_VERTEX_ARRAY: glVertexPointer(3, GL_FLOAT, 0, buffer); break; + case GL_TEXTURE_COORD_ARRAY: glTexCoordPointer(2, GL_FLOAT, 0, buffer); break; + case GL_NORMAL_ARRAY: if (buffer != NULL) glNormalPointer(GL_FLOAT, 0, buffer); break; + case GL_COLOR_ARRAY: if (buffer != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, buffer); break; + //case GL_INDEX_ARRAY: if (buffer != NULL) glIndexPointer(GL_SHORT, 0, buffer); break; // Indexed colors + default: break; + } +} + +// Disable vertex state pointer +void rlDisableStatePointer(int vertexAttribType) +{ + glDisableClientState(vertexAttribType); +} +#endif + +// Load vertex array object (VAO) +unsigned int rlLoadVertexArray(void) +{ + unsigned int vaoId = 0; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.vao) + { + glGenVertexArrays(1, &vaoId); + } +#endif + return vaoId; +} + +// Set vertex attribute +void rlSetVertexAttribute(unsigned int index, int compSize, int type, bool normalized, int stride, const void *pointer) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glVertexAttribPointer(index, compSize, type, normalized, stride, pointer); +#endif +} + +// Set vertex attribute divisor +void rlSetVertexAttributeDivisor(unsigned int index, int divisor) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glVertexAttribDivisor(index, divisor); +#endif +} + +// Unload vertex array object (VAO) +void rlUnloadVertexArray(unsigned int vaoId) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.ExtSupported.vao) + { + glBindVertexArray(0); + glDeleteVertexArrays(1, &vaoId); + TRACELOG(RL_LOG_INFO, "VAO: [ID %i] Unloaded vertex array data from VRAM (GPU)", vaoId); + } +#endif +} + +// Unload vertex buffer (VBO) +void rlUnloadVertexBuffer(unsigned int vboId) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDeleteBuffers(1, &vboId); + //TRACELOG(RL_LOG_INFO, "VBO: Unloaded vertex data from VRAM (GPU)"); +#endif +} + +// Shaders management +//----------------------------------------------------------------------------------------------- +// Load shader from code strings +// NOTE: If shader string is NULL, using default vertex/fragment shaders +unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode) +{ + unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int vertexShaderId = 0; + unsigned int fragmentShaderId = 0; + + // Compile vertex shader (if provided) + if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER); + // In case no vertex shader was provided or compilation failed, we use default vertex shader + if (vertexShaderId == 0) vertexShaderId = RLGL.State.defaultVShaderId; + + // Compile fragment shader (if provided) + if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER); + // In case no fragment shader was provided or compilation failed, we use default fragment shader + if (fragmentShaderId == 0) fragmentShaderId = RLGL.State.defaultFShaderId; + + // In case vertex and fragment shader are the default ones, no need to recompile, we can just assign the default shader program id + if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId; + else + { + // One of or both shader are new, we need to compile a new shader program + id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId); + + // We can detach and delete vertex/fragment shaders (if not default ones) + // NOTE: We detach shader before deletion to make sure memory is freed + if (vertexShaderId != RLGL.State.defaultVShaderId) + { + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, vertexShaderId); + glDeleteShader(vertexShaderId); + } + if (fragmentShaderId != RLGL.State.defaultFShaderId) + { + // WARNING: Shader program linkage could fail and returned id is 0 + if (id > 0) glDetachShader(id, fragmentShaderId); + glDeleteShader(fragmentShaderId); + } + + // In case shader program loading failed, we assign default shader + if (id == 0) + { + // In case shader loading fails, we return the default shader + TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader"); + id = RLGL.State.defaultShaderId; + } + /* + else + { + // Get available shader uniforms + // NOTE: This information is useful for debug... + int uniformCount = -1; + glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount); + + for (int i = 0; i < uniformCount; i++) + { + int namelen = -1; + int num = -1; + char name[256] = { 0 }; // Assume no variable names longer than 256 + GLenum type = GL_ZERO; + + // Get the name of the uniforms + glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name); + + name[namelen] = 0; + TRACELOGD("SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name)); + } + } + */ + } +#endif + + return id; +} + +// Compile custom shader and return shader id +unsigned int rlCompileShader(const char *shaderCode, int type) +{ + unsigned int shader = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + shader = glCreateShader(type); + glShaderSource(shader, 1, &shaderCode, NULL); + + GLint success = 0; + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (success == GL_FALSE) + { + switch (type) + { + case GL_VERTEX_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile vertex shader code", shader); break; + case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile fragment shader code", shader); break; + //case GL_GEOMETRY_SHADER: + #if defined(GRAPHICS_API_OPENGL_43) + case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile compute shader code", shader); break; + #endif + default: break; + } + + int maxLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + + if (maxLength > 0) + { + int length = 0; + char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); + glGetShaderInfoLog(shader, maxLength, &length, log); + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shader, log); + RL_FREE(log); + } + } + else + { + switch (type) + { + case GL_VERTEX_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Vertex shader compiled successfully", shader); break; + case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Fragment shader compiled successfully", shader); break; + //case GL_GEOMETRY_SHADER: + #if defined(GRAPHICS_API_OPENGL_43) + case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader compiled successfully", shader); break; + #endif + default: break; + } + } +#endif + + return shader; +} + +// Load custom shader strings and return program id +unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) +{ + unsigned int program = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + GLint success = 0; + program = glCreateProgram(); + + glAttachShader(program, vShaderId); + glAttachShader(program, fShaderId); + + // NOTE: Default attribute shader locations must be Bound before linking + glBindAttribLocation(program, 0, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION); + glBindAttribLocation(program, 1, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD); + glBindAttribLocation(program, 2, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL); + glBindAttribLocation(program, 3, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR); + glBindAttribLocation(program, 4, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT); + glBindAttribLocation(program, 5, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2); + + // NOTE: If some attrib name is no found on the shader, it locations becomes -1 + + glLinkProgram(program); + + // NOTE: All uniform variables are intitialised to 0 when a program links + + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link shader program", program); + + int maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + if (maxLength > 0) + { + int length = 0; + char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); + glGetProgramInfoLog(program, maxLength, &length, log); + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log); + RL_FREE(log); + } + + glDeleteProgram(program); + + program = 0; + } + else + { + // Get the size of compiled shader program (not available on OpenGL ES 2.0) + // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero. + //GLint binarySize = 0; + //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Program shader loaded successfully", program); + } +#endif + return program; +} + +// Unload shader program +void rlUnloadShaderProgram(unsigned int id) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + glDeleteProgram(id); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Unloaded shader program data from VRAM (GPU)", id); +#endif +} + +// Get shader location uniform +int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) +{ + int location = -1; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + location = glGetUniformLocation(shaderId, uniformName); + + if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName); + else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location); +#endif + return location; +} + +// Get shader location attribute +int rlGetLocationAttrib(unsigned int shaderId, const char *attribName) +{ + int location = -1; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + location = glGetAttribLocation(shaderId, attribName); + + if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName); + else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location); +#endif + return location; +} + +// Set shader value uniform +void rlSetUniform(int locIndex, const void *value, int uniformType, int count) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + switch (uniformType) + { + case RL_SHADER_UNIFORM_FLOAT: glUniform1fv(locIndex, count, (float *)value); break; + case RL_SHADER_UNIFORM_VEC2: glUniform2fv(locIndex, count, (float *)value); break; + case RL_SHADER_UNIFORM_VEC3: glUniform3fv(locIndex, count, (float *)value); break; + case RL_SHADER_UNIFORM_VEC4: glUniform4fv(locIndex, count, (float *)value); break; + case RL_SHADER_UNIFORM_INT: glUniform1iv(locIndex, count, (int *)value); break; + case RL_SHADER_UNIFORM_IVEC2: glUniform2iv(locIndex, count, (int *)value); break; + case RL_SHADER_UNIFORM_IVEC3: glUniform3iv(locIndex, count, (int *)value); break; + case RL_SHADER_UNIFORM_IVEC4: glUniform4iv(locIndex, count, (int *)value); break; + case RL_SHADER_UNIFORM_SAMPLER2D: glUniform1iv(locIndex, count, (int *)value); break; + default: TRACELOG(RL_LOG_WARNING, "SHADER: Failed to set uniform value, data type not recognized"); + } +#endif +} + +// Set shader value attribute +void rlSetVertexAttributeDefault(int locIndex, const void *value, int attribType, int count) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + switch (attribType) + { + case RL_SHADER_ATTRIB_FLOAT: if (count == 1) glVertexAttrib1fv(locIndex, (float *)value); break; + case RL_SHADER_ATTRIB_VEC2: if (count == 2) glVertexAttrib2fv(locIndex, (float *)value); break; + case RL_SHADER_ATTRIB_VEC3: if (count == 3) glVertexAttrib3fv(locIndex, (float *)value); break; + case RL_SHADER_ATTRIB_VEC4: if (count == 4) glVertexAttrib4fv(locIndex, (float *)value); break; + default: TRACELOG(RL_LOG_WARNING, "SHADER: Failed to set attrib default value, data type not recognized"); + } +#endif +} + +// Set shader value uniform matrix +void rlSetUniformMatrix(int locIndex, Matrix mat) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + float matfloat[16] = { + mat.m0, mat.m1, mat.m2, mat.m3, + mat.m4, mat.m5, mat.m6, mat.m7, + mat.m8, mat.m9, mat.m10, mat.m11, + mat.m12, mat.m13, mat.m14, mat.m15 + }; + glUniformMatrix4fv(locIndex, 1, false, matfloat); +#endif +} + +// Set shader value uniform sampler +void rlSetUniformSampler(int locIndex, unsigned int textureId) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // Check if texture is already active + for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) if (RLGL.State.activeTextureId[i] == textureId) return; + + // Register a new active texture for the internal batch system + // NOTE: Default texture is always activated as GL_TEXTURE0 + for (int i = 0; i < RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS; i++) + { + if (RLGL.State.activeTextureId[i] == 0) + { + glUniform1i(locIndex, 1 + i); // Activate new texture unit + RLGL.State.activeTextureId[i] = textureId; // Save texture id for binding on drawing + break; + } + } +#endif +} + +// Set shader currently active (id and locations) +void rlSetShader(unsigned int id, int *locs) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + if (RLGL.State.currentShaderId != id) + { + rlDrawRenderBatch(RLGL.currentBatch); + RLGL.State.currentShaderId = id; + RLGL.State.currentShaderLocs = locs; + } +#endif +} + +// Load compute shader program +unsigned int rlLoadComputeShaderProgram(unsigned int shaderId) +{ + unsigned int program = 0; + +#if defined(GRAPHICS_API_OPENGL_43) + GLint success = 0; + program = glCreateProgram(); + glAttachShader(program, shaderId); + glLinkProgram(program); + + // NOTE: All uniform variables are intitialised to 0 when a program links + + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success == GL_FALSE) + { + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link compute shader program", program); + + int maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + + if (maxLength > 0) + { + int length = 0; + char *log = (char *)RL_CALLOC(maxLength, sizeof(char)); + glGetProgramInfoLog(program, maxLength, &length, log); + TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log); + RL_FREE(log); + } + + glDeleteProgram(program); + + program = 0; + } + else + { + // Get the size of compiled shader program (not available on OpenGL ES 2.0) + // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero. + //GLint binarySize = 0; + //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", program); + } +#endif + + return program; +} + +// Dispatch compute shader (equivalent to *draw* for graphics pilepine) +void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glDispatchCompute(groupX, groupY, groupZ); +#endif +} + +// Load shader storage buffer object (SSBO) +unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHint) +{ + unsigned int ssbo = 0; + +#if defined(GRAPHICS_API_OPENGL_43) + glGenBuffers(1, &ssbo); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); + glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY); + glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, 0); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); +#endif + + return ssbo; +} + +// Unload shader storage buffer object (SSBO) +void rlUnloadShaderBuffer(unsigned int ssboId) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glDeleteBuffers(1, &ssboId); +#endif +} + +// Update SSBO buffer data +void rlUpdateShaderBuffer(unsigned int id, const void *data, unsigned int dataSize, unsigned int offset) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); + glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, dataSize, data); +#endif +} + +// Get SSBO buffer size +unsigned int rlGetShaderBufferSize(unsigned int id) +{ + long long size = 0; + +#if defined(GRAPHICS_API_OPENGL_43) + glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); + glGetInteger64v(GL_SHADER_STORAGE_BUFFER_SIZE, &size); +#endif + + return (size > 0)? (unsigned int)size : 0; +} + +// Read SSBO buffer data (GPU->CPU) +void rlReadShaderBuffer(unsigned int id, void *dest, unsigned int count, unsigned int offset) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glBindBuffer(GL_SHADER_STORAGE_BUFFER, id); + glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, count, dest); +#endif +} + +// Bind SSBO buffer +void rlBindShaderBuffer(unsigned int id, unsigned int index) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, index, id); +#endif +} + +// Copy SSBO buffer data +void rlCopyShaderBuffer(unsigned int destId, unsigned int srcId, unsigned int destOffset, unsigned int srcOffset, unsigned int count) +{ +#if defined(GRAPHICS_API_OPENGL_43) + glBindBuffer(GL_COPY_READ_BUFFER, srcId); + glBindBuffer(GL_COPY_WRITE_BUFFER, destId); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, srcOffset, destOffset, count); +#endif +} + +// Bind image texture +void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool readonly) +{ +#if defined(GRAPHICS_API_OPENGL_43) + unsigned int glInternalFormat = 0, glFormat = 0, glType = 0; + + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + glBindImageTexture(index, id, 0, 0, 0, readonly ? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat); +#endif +} + +// Matrix state management +//----------------------------------------------------------------------------------------- +// Get internal modelview matrix +Matrix rlGetMatrixModelview(void) +{ + Matrix matrix = rlMatrixIdentity(); +#if defined(GRAPHICS_API_OPENGL_11) + float mat[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, mat); + matrix.m0 = mat[0]; + matrix.m1 = mat[1]; + matrix.m2 = mat[2]; + matrix.m3 = mat[3]; + matrix.m4 = mat[4]; + matrix.m5 = mat[5]; + matrix.m6 = mat[6]; + matrix.m7 = mat[7]; + matrix.m8 = mat[8]; + matrix.m9 = mat[9]; + matrix.m10 = mat[10]; + matrix.m11 = mat[11]; + matrix.m12 = mat[12]; + matrix.m13 = mat[13]; + matrix.m14 = mat[14]; + matrix.m15 = mat[15]; +#else + matrix = RLGL.State.modelview; +#endif + return matrix; +} + +// Get internal projection matrix +Matrix rlGetMatrixProjection(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + float mat[16]; + glGetFloatv(GL_PROJECTION_MATRIX,mat); + Matrix m; + m.m0 = mat[0]; + m.m1 = mat[1]; + m.m2 = mat[2]; + m.m3 = mat[3]; + m.m4 = mat[4]; + m.m5 = mat[5]; + m.m6 = mat[6]; + m.m7 = mat[7]; + m.m8 = mat[8]; + m.m9 = mat[9]; + m.m10 = mat[10]; + m.m11 = mat[11]; + m.m12 = mat[12]; + m.m13 = mat[13]; + m.m14 = mat[14]; + m.m15 = mat[15]; + return m; +#else + return RLGL.State.projection; +#endif +} + +// Get internal accumulated transform matrix +Matrix rlGetMatrixTransform(void) +{ + Matrix mat = rlMatrixIdentity(); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + // TODO: Consider possible transform matrices in the RLGL.State.stack + // Is this the right order? or should we start with the first stored matrix instead of the last one? + //Matrix matStackTransform = rlMatrixIdentity(); + //for (int i = RLGL.State.stackCounter; i > 0; i--) matStackTransform = rlMatrixMultiply(RLGL.State.stack[i], matStackTransform); + mat = RLGL.State.transform; +#endif + return mat; +} + +// Get internal projection matrix for stereo render (selected eye) +RLAPI Matrix rlGetMatrixProjectionStereo(int eye) +{ + Matrix mat = rlMatrixIdentity(); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + mat = RLGL.State.projectionStereo[eye]; +#endif + return mat; +} + +// Get internal view offset matrix for stereo render (selected eye) +RLAPI Matrix rlGetMatrixViewOffsetStereo(int eye) +{ + Matrix mat = rlMatrixIdentity(); +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + mat = RLGL.State.viewOffsetStereo[eye]; +#endif + return mat; +} + +// Set a custom modelview matrix (replaces internal modelview matrix) +void rlSetMatrixModelview(Matrix view) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.modelview = view; +#endif +} + +// Set a custom projection matrix (replaces internal projection matrix) +void rlSetMatrixProjection(Matrix projection) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.projection = projection; +#endif +} + +// Set eyes projection matrices for stereo rendering +void rlSetMatrixProjectionStereo(Matrix right, Matrix left) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.projectionStereo[0] = right; + RLGL.State.projectionStereo[1] = left; +#endif +} + +// Set eyes view offsets matrices for stereo rendering +void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + RLGL.State.viewOffsetStereo[0] = right; + RLGL.State.viewOffsetStereo[1] = left; +#endif +} + +// Load and draw a quad in NDC +void rlLoadDrawQuad(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int quadVAO = 0; + unsigned int quadVBO = 0; + + float vertices[] = { + // Positions Texcoords + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + }; + + // Gen VAO to contain VBO + glGenVertexArrays(1, &quadVAO); + glBindVertexArray(quadVAO); + + // Gen and fill vertex buffer (VBO) + glGenBuffers(1, &quadVBO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW); + + // Bind vertex attributes (position, texcoords) + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0); // Positions + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)(3*sizeof(float))); // Texcoords + + // Draw quad + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + + // Delete buffers (VBO and VAO) + glDeleteBuffers(1, &quadVBO); + glDeleteVertexArrays(1, &quadVAO); +#endif +} + +// Load and draw a cube in NDC +void rlLoadDrawCube(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + unsigned int cubeVAO = 0; + unsigned int cubeVBO = 0; + + float vertices[] = { + // Positions Normals Texcoords + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f + }; + + // Gen VAO to contain VBO + glGenVertexArrays(1, &cubeVAO); + glBindVertexArray(cubeVAO); + + // Gen and fill vertex buffer (VBO) + glGenBuffers(1, &cubeVBO); + glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + // Bind vertex attributes (position, normals, texcoords) + glBindVertexArray(cubeVAO); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)0); // Positions + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(3*sizeof(float))); // Normals + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(6*sizeof(float))); // Texcoords + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + // Draw cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); + + // Delete VBO and VAO + glDeleteBuffers(1, &cubeVBO); + glDeleteVertexArrays(1, &cubeVAO); +#endif +} + +// Get name string for pixel format +const char *rlGetPixelFormatName(unsigned int format) +{ + switch (format) + { + case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: return "GRAYSCALE"; break; // 8 bit per pixel (no alpha) + case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: return "GRAY_ALPHA"; break; // 8*2 bpp (2 channels) + case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: return "R5G6B5"; break; // 16 bpp + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: return "R8G8B8"; break; // 24 bpp + case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: return "R5G5B5A1"; break; // 16 bpp (1 bit alpha) + case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: return "R4G4B4A4"; break; // 16 bpp (4 bit alpha) + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: return "R8G8B8A8"; break; // 32 bpp + case RL_PIXELFORMAT_UNCOMPRESSED_R32: return "R32"; break; // 32 bpp (1 channel - float) + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: return "R32G32B32"; break; // 32*3 bpp (3 channels - float) + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: return "R32G32B32A32"; break; // 32*4 bpp (4 channels - float) + case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: return "DXT1_RGB"; break; // 4 bpp (no alpha) + case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: return "DXT1_RGBA"; break; // 4 bpp (1 bit alpha) + case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: return "DXT3_RGBA"; break; // 8 bpp + case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: return "DXT5_RGBA"; break; // 8 bpp + case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: return "ETC1_RGB"; break; // 4 bpp + case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB: return "ETC2_RGB"; break; // 4 bpp + case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: return "ETC2_RGBA"; break; // 8 bpp + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB: return "PVRT_RGB"; break; // 4 bpp + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: return "PVRT_RGBA"; break; // 4 bpp + case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: return "ASTC_4x4_RGBA"; break; // 8 bpp + case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: return "ASTC_8x8_RGBA"; break; // 2 bpp + default: return "UNKNOWN"; break; + } +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +// Load default shader (just vertex positioning and texture coloring) +// NOTE: This shader program is used for internal buffers +// NOTE: Loaded: RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs +static void rlLoadShaderDefault(void) +{ + RLGL.State.defaultShaderLocs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int)); + + // NOTE: All locations must be reseted to -1 (no location) + for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) RLGL.State.defaultShaderLocs[i] = -1; + + // Vertex shader directly defined, no external file required + const char *defaultVShaderCode = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" + "attribute vec3 vertexPosition; \n" + "attribute vec2 vertexTexCoord; \n" + "attribute vec4 vertexColor; \n" + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" +#elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec3 vertexPosition; \n" + "in vec2 vertexTexCoord; \n" + "in vec4 vertexColor; \n" + "out vec2 fragTexCoord; \n" + "out vec4 fragColor; \n" +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + "#version 100 \n" + "attribute vec3 vertexPosition; \n" + "attribute vec2 vertexTexCoord; \n" + "attribute vec4 vertexColor; \n" + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" +#endif + "uniform mat4 mvp; \n" + "void main() \n" + "{ \n" + " fragTexCoord = vertexTexCoord; \n" + " fragColor = vertexColor; \n" + " gl_Position = mvp*vec4(vertexPosition, 1.0); \n" + "} \n"; + + // Fragment shader directly defined, no external file required + const char *defaultFShaderCode = +#if defined(GRAPHICS_API_OPENGL_21) + "#version 120 \n" + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" + " gl_FragColor = texelColor*colDiffuse*fragColor; \n" + "} \n"; +#elif defined(GRAPHICS_API_OPENGL_33) + "#version 330 \n" + "in vec2 fragTexCoord; \n" + "in vec4 fragColor; \n" + "out vec4 finalColor; \n" + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" + " vec4 texelColor = texture(texture0, fragTexCoord); \n" + " finalColor = texelColor*colDiffuse*fragColor; \n" + "} \n"; +#endif +#if defined(GRAPHICS_API_OPENGL_ES2) + "#version 100 \n" + "precision mediump float; \n" // Precision required for OpenGL ES2 (WebGL) + "varying vec2 fragTexCoord; \n" + "varying vec4 fragColor; \n" + "uniform sampler2D texture0; \n" + "uniform vec4 colDiffuse; \n" + "void main() \n" + "{ \n" + " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" + " gl_FragColor = texelColor*colDiffuse*fragColor; \n" + "} \n"; +#endif + + // NOTE: Compiled vertex/fragment shaders are not deleted, + // they are kept for re-use as default shaders in case some shader loading fails + RLGL.State.defaultVShaderId = rlCompileShader(defaultVShaderCode, GL_VERTEX_SHADER); // Compile default vertex shader + RLGL.State.defaultFShaderId = rlCompileShader(defaultFShaderCode, GL_FRAGMENT_SHADER); // Compile default fragment shader + + RLGL.State.defaultShaderId = rlLoadShaderProgram(RLGL.State.defaultVShaderId, RLGL.State.defaultFShaderId); + + if (RLGL.State.defaultShaderId > 0) + { + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Default shader loaded successfully", RLGL.State.defaultShaderId); + + // Set default shader locations: attributes locations + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_POSITION] = glGetAttribLocation(RLGL.State.defaultShaderId, "vertexPosition"); + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(RLGL.State.defaultShaderId, "vertexTexCoord"); + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_COLOR] = glGetAttribLocation(RLGL.State.defaultShaderId, "vertexColor"); + + // Set default shader locations: uniform locations + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MATRIX_MVP] = glGetUniformLocation(RLGL.State.defaultShaderId, "mvp"); + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_COLOR_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, "colDiffuse"); + RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MAP_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, "texture0"); + } + else TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to load default shader", RLGL.State.defaultShaderId); +} + +// Unload default shader +// NOTE: Unloads: RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs +static void rlUnloadShaderDefault(void) +{ + glUseProgram(0); + + glDetachShader(RLGL.State.defaultShaderId, RLGL.State.defaultVShaderId); + glDetachShader(RLGL.State.defaultShaderId, RLGL.State.defaultFShaderId); + glDeleteShader(RLGL.State.defaultVShaderId); + glDeleteShader(RLGL.State.defaultFShaderId); + + glDeleteProgram(RLGL.State.defaultShaderId); + + RL_FREE(RLGL.State.defaultShaderLocs); + + TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Default shader unloaded successfully", RLGL.State.defaultShaderId); +} + +#if defined(RLGL_SHOW_GL_DETAILS_INFO) +// Get compressed format official GL identifier name +static char *rlGetCompressedFormatName(int format) +{ + switch (format) + { + // GL_EXT_texture_compression_s3tc + case 0x83F0: return "GL_COMPRESSED_RGB_S3TC_DXT1_EXT"; break; + case 0x83F1: return "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT"; break; + case 0x83F2: return "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT"; break; + case 0x83F3: return "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT"; break; + // GL_3DFX_texture_compression_FXT1 + case 0x86B0: return "GL_COMPRESSED_RGB_FXT1_3DFX"; break; + case 0x86B1: return "GL_COMPRESSED_RGBA_FXT1_3DFX"; break; + // GL_IMG_texture_compression_pvrtc + case 0x8C00: return "GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG"; break; + case 0x8C01: return "GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG"; break; + case 0x8C02: return "GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG"; break; + case 0x8C03: return "GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG"; break; + // GL_OES_compressed_ETC1_RGB8_texture + case 0x8D64: return "GL_ETC1_RGB8_OES"; break; + // GL_ARB_texture_compression_rgtc + case 0x8DBB: return "GL_COMPRESSED_RED_RGTC1"; break; + case 0x8DBC: return "GL_COMPRESSED_SIGNED_RED_RGTC1"; break; + case 0x8DBD: return "GL_COMPRESSED_RG_RGTC2"; break; + case 0x8DBE: return "GL_COMPRESSED_SIGNED_RG_RGTC2"; break; + // GL_ARB_texture_compression_bptc + case 0x8E8C: return "GL_COMPRESSED_RGBA_BPTC_UNORM_ARB"; break; + case 0x8E8D: return "GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB"; break; + case 0x8E8E: return "GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB"; break; + case 0x8E8F: return "GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB"; break; + // GL_ARB_ES3_compatibility + case 0x9274: return "GL_COMPRESSED_RGB8_ETC2"; break; + case 0x9275: return "GL_COMPRESSED_SRGB8_ETC2"; break; + case 0x9276: return "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2"; break; + case 0x9277: return "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2"; break; + case 0x9278: return "GL_COMPRESSED_RGBA8_ETC2_EAC"; break; + case 0x9279: return "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC"; break; + case 0x9270: return "GL_COMPRESSED_R11_EAC"; break; + case 0x9271: return "GL_COMPRESSED_SIGNED_R11_EAC"; break; + case 0x9272: return "GL_COMPRESSED_RG11_EAC"; break; + case 0x9273: return "GL_COMPRESSED_SIGNED_RG11_EAC"; break; + // GL_KHR_texture_compression_astc_hdr + case 0x93B0: return "GL_COMPRESSED_RGBA_ASTC_4x4_KHR"; break; + case 0x93B1: return "GL_COMPRESSED_RGBA_ASTC_5x4_KHR"; break; + case 0x93B2: return "GL_COMPRESSED_RGBA_ASTC_5x5_KHR"; break; + case 0x93B3: return "GL_COMPRESSED_RGBA_ASTC_6x5_KHR"; break; + case 0x93B4: return "GL_COMPRESSED_RGBA_ASTC_6x6_KHR"; break; + case 0x93B5: return "GL_COMPRESSED_RGBA_ASTC_8x5_KHR"; break; + case 0x93B6: return "GL_COMPRESSED_RGBA_ASTC_8x6_KHR"; break; + case 0x93B7: return "GL_COMPRESSED_RGBA_ASTC_8x8_KHR"; break; + case 0x93B8: return "GL_COMPRESSED_RGBA_ASTC_10x5_KHR"; break; + case 0x93B9: return "GL_COMPRESSED_RGBA_ASTC_10x6_KHR"; break; + case 0x93BA: return "GL_COMPRESSED_RGBA_ASTC_10x8_KHR"; break; + case 0x93BB: return "GL_COMPRESSED_RGBA_ASTC_10x10_KHR"; break; + case 0x93BC: return "GL_COMPRESSED_RGBA_ASTC_12x10_KHR"; break; + case 0x93BD: return "GL_COMPRESSED_RGBA_ASTC_12x12_KHR"; break; + case 0x93D0: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR"; break; + case 0x93D1: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR"; break; + case 0x93D2: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR"; break; + case 0x93D3: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR"; break; + case 0x93D4: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR"; break; + case 0x93D5: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR"; break; + case 0x93D6: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR"; break; + case 0x93D7: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR"; break; + case 0x93D8: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR"; break; + case 0x93D9: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR"; break; + case 0x93DA: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR"; break; + case 0x93DB: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR"; break; + case 0x93DC: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR"; break; + case 0x93DD: return "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR"; break; + default: return "GL_COMPRESSED_UNKNOWN"; break; + } +} +#endif // RLGL_SHOW_GL_DETAILS_INFO + +#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 + +// Get pixel data size in bytes (image or texture) +// NOTE: Size depends on pixel format +static int rlGetPixelDataSize(int width, int height, int format) +{ + int dataSize = 0; // Size in bytes + int bpp = 0; // Bits per pixel + + switch (format) + { + case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break; + case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case RL_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case RL_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; + case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: + case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: + case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: + case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB: + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB: + case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break; + case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: + case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: + case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: + case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break; + case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; + default: break; + } + + dataSize = width*height*bpp/8; // Total data size in bytes + + // Most compressed formats works on 4x4 blocks, + // if texture is smaller, minimum dataSize is 8 or 16 + if ((width < 4) && (height < 4)) + { + if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8; + else if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16; + } + + return dataSize; +} + +// Auxiliar math functions + +// Get identity matrix +static Matrix rlMatrixIdentity(void) +{ + Matrix result = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + return result; +} + +// Get two matrix multiplication +// NOTE: When multiplying matrices... the order matters! +static Matrix rlMatrixMultiply(Matrix left, Matrix right) +{ + Matrix result = { 0 }; + + result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; + result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; + result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; + result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; + result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; + result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; + result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; + result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; + result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; + result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; + result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; + result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; + result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; + result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; + result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; + result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; + + return result; +} + +#endif // RLGL_IMPLEMENTATION diff --git a/build/modules/win32_multimedia.core b/modules/win32_multimedia.core similarity index 96% rename from build/modules/win32_multimedia.core rename to modules/win32_multimedia.core index d6e6f68..f8f7846 100644 --- a/build/modules/win32_multimedia.core +++ b/modules/win32_multimedia.core @@ -1,355 +1,355 @@ -#import "KERNEL32.core" -#import "GDI32.core" -#import "USER32.core" -#import "WINMM.core" -#import "OS$OS.core" - -Platform :: struct - bitmap: WIN32_Bitmap - window_dc: HDC - window: HWND - good_scheduling: bool - -WIN32_Bitmap :: struct - size: Vec2I - data: *U32 - hdc: HDC - dib: HBITMAP - compatible_dc: HDC - -IsValidBitmap :: (b: *WIN32_Bitmap): bool - result := b.data != 0 - return result - -CreateBitmap :: (for_dc: HDC, size: Vec2I, bottom_up: bool = true): WIN32_Bitmap - result: WIN32_Bitmap = {size = size} - if bottom_up == false - result.size.y = -result.size.y - - header_size: U32 = sizeof(BITMAPINFOHEADER) - Assert(header_size == 40) - bminfo := BITMAPINFO{ - BITMAPINFOHEADER{ - biSize = header_size, - biWidth = size.x->LONG, - biHeight = size.y->LONG, - biPlanes = 1, - biBitCount = 32, - biCompression = BI_RGB, - biXPelsPerMeter = 1, - biYPelsPerMeter = 1, - } - } - - result.dib = CreateDIBSection(for_dc, &bminfo, DIB_RGB_COLORS, (&result.data)->**void, 0, 0) - result.hdc = CreateCompatibleDC(for_dc) - result.compatible_dc = for_dc - return result - -DestroyBitmap :: (b: *WIN32_Bitmap) - if IsValidBitmap(b) - DeleteDC(b.hdc) - DeleteObject(b.dib) - ZeroMemory(b, sizeof(WIN32_Bitmap)) - -DrawBitmapInCompatibleDC :: (b: *WIN32_Bitmap) - if IsValidBitmap(b) - SelectObject(b.hdc, b.dib) - BitBlt(b.compatible_dc, 0, 0, b.size.x->int, b.size.y->int, b.hdc, 0, 0, SRCCOPY) - -GetWindowStyle :: (resizable: bool): DWORD - style: DWORD = WS_SYSMENU | WS_OVERLAPPED | WS_CAPTION - if resizable ;; style |= WS_MAXIMIZEBOX | WS_THICKFRAME | WS_MINIMIZEBOX - return style - -GetWindowSize :: (window: HWND): Vec2I - result: Vec2I - window_rect: RECT - GetClientRect(window, &window_rect) - result.x = (window_rect.right - window_rect.left)->int - result.y = (window_rect.bottom - window_rect.top)->int - return result - -GetWindowPos :: (window: HWND): Vec2I - pos: Point - ClientToScreen(window, &pos) - return {pos.x, pos.y} - -AdjustWindowRect :: (window: HWND, style: DWORD, rect: *RECT): void - FALSE :: 0 - // if window == 0 - // dpi := GetDpiForWindow(window) - // AdjustWindowRectExForDpi(rect, style, FALSE, 0, dpi) - // else - AdjustWindowRectEx(rect, style, FALSE, 0) - -SetWindowSize :: (window: HWND, style: DWORD, size: Vec2I): void - rect := RECT{ 0, 0, size.x->LONG, size.y->LONG } - AdjustWindowRect(window, style, &rect) - SetWindowPos(window, HWND_TOP, 0, 0, (rect.right - rect.left)->int, (rect.bottom - rect.top)->int, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER) - -SetWindowPosition :: (window: HWND, style: DWORD, pos: Vec2I): void - rect := RECT{ pos.x->LONG, pos.y->LONG, pos.x->LONG, pos.y->LONG } - AdjustWindowRect(window, style, &rect) - SetWindowPos(window, 0, rect.left, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE) - -StartMultimedia :: ( - x: int = 1280, y: int = 720, - title: String = "Hello people!", - window_resizable: bool = false, - target_ms: F64 = 0.0166666 -) - Mu.time.delta = target_ms - if timeBeginPeriod(1) == TIMERR_NOERROR - Mu.os.good_scheduling = true - - dpi_aware := SetProcessDPIAware() - Assert(dpi_aware != 0) - - Mu.time.start = Time() - - hInstance := GetModuleHandleA(0) - window_name := StringToString16(&Mu.frame_arena, title) - w := WNDCLASSW{ - lpfnWndProc = WindowProc, - hInstance = hInstance, - lpszClassName = window_name.str, - // style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW, - } - Assert(RegisterClassW(&w) != 0) - - style: DWORD = GetWindowStyle(window_resizable) - Mu.os.window = CreateWindowExW( - dwExStyle = 0, hWndParent = 0, hMenu = 0, lpParam = 0, - X = CW_USEDEFAULT, Y = CW_USEDEFAULT, nWidth = x->int, nHeight = y->int, - lpClassName = window_name.str, - lpWindowName = window_name.str, - dwStyle = style, - hInstance = hInstance - ) - Assert(Mu.os.window != 0) - SetWindowSize(Mu.os.window, style, {x,y}) - ShowWindow(Mu.os.window, SW_SHOW) - - size := GetWindowSize(Mu.os.window) - Assert(size.x == x && size.y == y) - - Mu.os.window_dc = GetDC(Mu.os.window) - Mu.os.bitmap = CreateBitmap(Mu.os.window_dc, size) - - Mu.window.resizable = window_resizable - Mu.screen = Mu.os.bitmap.data - Mu.window.x = size.x - Mu.window.y = size.y - Mu.window.size = size - Mu.window.sizef.x = Mu.window.x->F32 - Mu.window.sizef.y = Mu.window.y->F32 - -UpdateMultimedia :: (): bool - DrawBitmapInCompatibleDC(&Mu.os.bitmap) - - msg: MSG - for PeekMessageW(&msg, Mu.os.window, 0, 0, PM_REMOVE) == 1 - TranslateMessage(&msg) - DispatchMessageW(&msg) - - size := GetWindowSize(Mu.os.window) - if size.x != Mu.window.x || size.y != Mu.window.y - DestroyBitmap(&Mu.os.bitmap) - Mu.os.bitmap = CreateBitmap(Mu.os.window_dc, size) - - Mu.screen = Mu.os.bitmap.data - Mu.window.x = size.x - Mu.window.y = size.y - Mu.window.sizef.x = Mu.window.x->F32 - Mu.window.sizef.y = Mu.window.y->F32 - Mu.window.size = size - - - Mu.frame_count += 1 - frame_time := Time() - Mu.time.frame_start - Mu.time.total += frame_time - if frame_time < Mu.time.delta - if Mu.os.good_scheduling - time_to_sleep := (Mu.time.delta - frame_time) * 1000 - if time_to_sleep > 0 - time_to_sleep_dword := time_to_sleep->DWORD - // @check if time_to_sleep_dword truncates down - Sleep(time_to_sleep_dword) - - new_frame_time := Time() - for new_frame_time < Mu.time.delta - new_frame_time = Time() - Mu.time.frame_start - - Mu.time.frame_start = Time() - return !Mu.quit - -WindowProc :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRESULT - result: LRESULT - if msg == WM_DESTROY - // @todo: Add destroy window - PostQuitMessage(0) - return 0 - elif msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN - key := MapVKToKey(wparam) - Mu.key[key].down = true - elif msg == WM_KEYUP || msg == WM_SYSKEYUP - key := MapVKToKey(wparam) - Mu.key[key].down = false - elif msg == WM_LBUTTONDOWN ;; Mu.mouse.left.down = true - elif msg == WM_LBUTTONUP ;; Mu.mouse.left.down = false - elif msg == WM_RBUTTONDOWN ;; Mu.mouse.right.down = true - elif msg == WM_RBUTTONUP ;; Mu.mouse.right.down = false - elif msg == WM_MBUTTONDOWN ;; Mu.mouse.middle.down = true - elif msg == WM_MBUTTONUP ;; Mu.mouse.middle.down = false - elif msg == WM_MOUSEWHEEL - if wparam->int > 0 - Mu.mouse.wheel = 1 - else - Mu.mouse.wheel = -1 - // Case(WM_CHAR){ - // Utf32Result utf32 = Utf16ToUtf32((u16 *)&wparam, 1); - // if(!utf32.error){ - // int index = OS->input_count; - // OS->input_count = ClampTop(OS->input_count + 1, (int)(ArrayCount(OS->input) - 1)); - // OS->input[index] = utf32.out_str; - // } - // }Break; - - - else;; result = DefWindowProcW(hwnd, msg, wparam, lparam) - - return result - - -/*# -mapping = [ - ["Up", "VK_UP"], - ["Down", "VK_DOWN"], - ["Left", "VK_LEFT"], - ["Right", "VK_RIGHT"], - ["Escape", "VK_ESCAPE"], - ["Control", "VK_CONTROL"], - ["Backspace", "VK_BACK"], - ["Alt", "VK_MENU"], - ["Shift", "VK_SHIFT"], - ["Tab", "VK_TAB"], - ["F1", "VK_F1"], - ["F2", "VK_F2"], - ["F3", "VK_F3"], - ["F4", "VK_F4"], - ["F5", "VK_F5"], - ["F6", "VK_F6"], - ["F7", "VK_F7"], - ["F8", "VK_F8"], - ["F9", "VK_F9"], - ["F10", "VK_F10"], - ["F11", "VK_F11"], - ["F12", "VK_F12"], - ["A", "'A'"], - ["B", "'B'"], - ["C", "'C'"], - ["D", "'D'"], - ["E", "'E'"], - ["F", "'F'"], - ["G", "'G'"], - ["H", "'H'"], - ["I", "'I'"], - ["J", "'J'"], - ["K", "'K'"], - ["L", "'L'"], - ["M", "'M'"], - ["N", "'N'"], - ["O", "'O'"], - ["P", "'P'"], - ["Q", "'Q'"], - ["R", "'R'"], - ["S", "'S'"], - ["T", "'T'"], - ["U", "'U'"], - ["V", "'V'"], - ["W", "'W'"], - ["X", "'X'"], - ["Y", "'Y'"], - ["Z", "'Z'"], - ["K0", "'0'"], - ["K1", "'1'"], - ["K2", "'2'"], - ["K3", "'3'"], - ["K4", "'4'"], - ["K5", "'5'"], - ["K6", "'6'"], - ["K7", "'7'"], - ["K8", "'8'"], - ["K9", "'9'"], -] - - -print("MapVKToKey :: (vk: WPARAM): Key") -el = "" -for val,map in mapping: - print(f" {el}if vk == {map} ;; return Key.{val}") - el = "el" -print(" return Key.None") -*/ -MapVKToKey :: (vk: WPARAM): Key - if vk == VK_UP ;; return Key.Up - elif vk == VK_DOWN ;; return Key.Down - elif vk == VK_LEFT ;; return Key.Left - elif vk == VK_RIGHT ;; return Key.Right - elif vk == VK_ESCAPE ;; return Key.Escape - elif vk == VK_CONTROL ;; return Key.Control - elif vk == VK_BACK ;; return Key.Backspace - elif vk == VK_MENU ;; return Key.Alt - elif vk == VK_SHIFT ;; return Key.Shift - elif vk == VK_TAB ;; return Key.Tab - elif vk == VK_F1 ;; return Key.F1 - elif vk == VK_F2 ;; return Key.F2 - elif vk == VK_F3 ;; return Key.F3 - elif vk == VK_F4 ;; return Key.F4 - elif vk == VK_F5 ;; return Key.F5 - elif vk == VK_F6 ;; return Key.F6 - elif vk == VK_F7 ;; return Key.F7 - elif vk == VK_F8 ;; return Key.F8 - elif vk == VK_F9 ;; return Key.F9 - elif vk == VK_F10 ;; return Key.F10 - elif vk == VK_F11 ;; return Key.F11 - elif vk == VK_F12 ;; return Key.F12 - elif vk == 'A' ;; return Key.A - elif vk == 'B' ;; return Key.B - elif vk == 'C' ;; return Key.C - elif vk == 'D' ;; return Key.D - elif vk == 'E' ;; return Key.E - elif vk == 'F' ;; return Key.F - elif vk == 'G' ;; return Key.G - elif vk == 'H' ;; return Key.H - elif vk == 'I' ;; return Key.I - elif vk == 'J' ;; return Key.J - elif vk == 'K' ;; return Key.K - elif vk == 'L' ;; return Key.L - elif vk == 'M' ;; return Key.M - elif vk == 'N' ;; return Key.N - elif vk == 'O' ;; return Key.O - elif vk == 'P' ;; return Key.P - elif vk == 'Q' ;; return Key.Q - elif vk == 'R' ;; return Key.R - elif vk == 'S' ;; return Key.S - elif vk == 'T' ;; return Key.T - elif vk == 'U' ;; return Key.U - elif vk == 'V' ;; return Key.V - elif vk == 'W' ;; return Key.W - elif vk == 'X' ;; return Key.X - elif vk == 'Y' ;; return Key.Y - elif vk == 'Z' ;; return Key.Z - elif vk == '0' ;; return Key.K0 - elif vk == '1' ;; return Key.K1 - elif vk == '2' ;; return Key.K2 - elif vk == '3' ;; return Key.K3 - elif vk == '4' ;; return Key.K4 - elif vk == '5' ;; return Key.K5 - elif vk == '6' ;; return Key.K6 - elif vk == '7' ;; return Key.K7 - elif vk == '8' ;; return Key.K8 - elif vk == '9' ;; return Key.K9 - return Key.None +#import "KERNEL32.core" +#import "GDI32.core" +#import "USER32.core" +#import "WINMM.core" +#import "OS$OS.core" + +Platform :: struct + bitmap: WIN32_Bitmap + window_dc: HDC + window: HWND + good_scheduling: bool + +WIN32_Bitmap :: struct + size: Vec2I + data: *U32 + hdc: HDC + dib: HBITMAP + compatible_dc: HDC + +IsValidBitmap :: (b: *WIN32_Bitmap): bool + result := b.data != 0 + return result + +CreateBitmap :: (for_dc: HDC, size: Vec2I, bottom_up: bool = true): WIN32_Bitmap + result: WIN32_Bitmap = {size = size} + if bottom_up == false + result.size.y = -result.size.y + + header_size: U32 = sizeof(BITMAPINFOHEADER) + Assert(header_size == 40) + bminfo := BITMAPINFO{ + BITMAPINFOHEADER{ + biSize = header_size, + biWidth = size.x->LONG, + biHeight = size.y->LONG, + biPlanes = 1, + biBitCount = 32, + biCompression = BI_RGB, + biXPelsPerMeter = 1, + biYPelsPerMeter = 1, + } + } + + result.dib = CreateDIBSection(for_dc, &bminfo, DIB_RGB_COLORS, (&result.data)->**void, 0, 0) + result.hdc = CreateCompatibleDC(for_dc) + result.compatible_dc = for_dc + return result + +DestroyBitmap :: (b: *WIN32_Bitmap) + if IsValidBitmap(b) + DeleteDC(b.hdc) + DeleteObject(b.dib) + ZeroMemory(b, sizeof(WIN32_Bitmap)) + +DrawBitmapInCompatibleDC :: (b: *WIN32_Bitmap) + if IsValidBitmap(b) + SelectObject(b.hdc, b.dib) + BitBlt(b.compatible_dc, 0, 0, b.size.x->int, b.size.y->int, b.hdc, 0, 0, SRCCOPY) + +GetWindowStyle :: (resizable: bool): DWORD + style: DWORD = WS_SYSMENU | WS_OVERLAPPED | WS_CAPTION + if resizable ;; style |= WS_MAXIMIZEBOX | WS_THICKFRAME | WS_MINIMIZEBOX + return style + +GetWindowSize :: (window: HWND): Vec2I + result: Vec2I + window_rect: RECT + GetClientRect(window, &window_rect) + result.x = (window_rect.right - window_rect.left)->int + result.y = (window_rect.bottom - window_rect.top)->int + return result + +GetWindowPos :: (window: HWND): Vec2I + pos: Point + ClientToScreen(window, &pos) + return {pos.x, pos.y} + +AdjustWindowRect :: (window: HWND, style: DWORD, rect: *RECT): void + FALSE :: 0 + // if window == 0 + // dpi := GetDpiForWindow(window) + // AdjustWindowRectExForDpi(rect, style, FALSE, 0, dpi) + // else + AdjustWindowRectEx(rect, style, FALSE, 0) + +SetWindowSize :: (window: HWND, style: DWORD, size: Vec2I): void + rect := RECT{ 0, 0, size.x->LONG, size.y->LONG } + AdjustWindowRect(window, style, &rect) + SetWindowPos(window, HWND_TOP, 0, 0, (rect.right - rect.left)->int, (rect.bottom - rect.top)->int, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER) + +SetWindowPosition :: (window: HWND, style: DWORD, pos: Vec2I): void + rect := RECT{ pos.x->LONG, pos.y->LONG, pos.x->LONG, pos.y->LONG } + AdjustWindowRect(window, style, &rect) + SetWindowPos(window, 0, rect.left, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE) + +StartMultimedia :: ( + x: int = 1280, y: int = 720, + title: String = "Hello people!", + window_resizable: bool = false, + target_ms: F64 = 0.0166666 +) + Mu.time.delta = target_ms + if timeBeginPeriod(1) == TIMERR_NOERROR + Mu.os.good_scheduling = true + + dpi_aware := SetProcessDPIAware() + Assert(dpi_aware != 0) + + Mu.time.start = Time() + + hInstance := GetModuleHandleA(0) + window_name := StringToString16(&Mu.frame_arena, title) + w := WNDCLASSW{ + lpfnWndProc = WindowProc, + hInstance = hInstance, + lpszClassName = window_name.str, + // style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW, + } + Assert(RegisterClassW(&w) != 0) + + style: DWORD = GetWindowStyle(window_resizable) + Mu.os.window = CreateWindowExW( + dwExStyle = 0, hWndParent = 0, hMenu = 0, lpParam = 0, + X = CW_USEDEFAULT, Y = CW_USEDEFAULT, nWidth = x->int, nHeight = y->int, + lpClassName = window_name.str, + lpWindowName = window_name.str, + dwStyle = style, + hInstance = hInstance + ) + Assert(Mu.os.window != 0) + SetWindowSize(Mu.os.window, style, {x,y}) + ShowWindow(Mu.os.window, SW_SHOW) + + size := GetWindowSize(Mu.os.window) + Assert(size.x == x && size.y == y) + + Mu.os.window_dc = GetDC(Mu.os.window) + Mu.os.bitmap = CreateBitmap(Mu.os.window_dc, size) + + Mu.window.resizable = window_resizable + Mu.screen = Mu.os.bitmap.data + Mu.window.x = size.x + Mu.window.y = size.y + Mu.window.size = size + Mu.window.sizef.x = Mu.window.x->F32 + Mu.window.sizef.y = Mu.window.y->F32 + +UpdateMultimedia :: (): bool + DrawBitmapInCompatibleDC(&Mu.os.bitmap) + + msg: MSG + for PeekMessageW(&msg, Mu.os.window, 0, 0, PM_REMOVE) == 1 + TranslateMessage(&msg) + DispatchMessageW(&msg) + + size := GetWindowSize(Mu.os.window) + if size.x != Mu.window.x || size.y != Mu.window.y + DestroyBitmap(&Mu.os.bitmap) + Mu.os.bitmap = CreateBitmap(Mu.os.window_dc, size) + + Mu.screen = Mu.os.bitmap.data + Mu.window.x = size.x + Mu.window.y = size.y + Mu.window.sizef.x = Mu.window.x->F32 + Mu.window.sizef.y = Mu.window.y->F32 + Mu.window.size = size + + + Mu.frame_count += 1 + frame_time := Time() - Mu.time.frame_start + Mu.time.total += frame_time + if frame_time < Mu.time.delta + if Mu.os.good_scheduling + time_to_sleep := (Mu.time.delta - frame_time) * 1000 + if time_to_sleep > 0 + time_to_sleep_dword := time_to_sleep->DWORD + // @check if time_to_sleep_dword truncates down + Sleep(time_to_sleep_dword) + + new_frame_time := Time() + for new_frame_time < Mu.time.delta + new_frame_time = Time() - Mu.time.frame_start + + Mu.time.frame_start = Time() + return !Mu.quit + +WindowProc :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRESULT + result: LRESULT + if msg == WM_DESTROY + // @todo: Add destroy window + PostQuitMessage(0) + return 0 + elif msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN + key := MapVKToKey(wparam) + Mu.key[key].down = true + elif msg == WM_KEYUP || msg == WM_SYSKEYUP + key := MapVKToKey(wparam) + Mu.key[key].down = false + elif msg == WM_LBUTTONDOWN ;; Mu.mouse.left.down = true + elif msg == WM_LBUTTONUP ;; Mu.mouse.left.down = false + elif msg == WM_RBUTTONDOWN ;; Mu.mouse.right.down = true + elif msg == WM_RBUTTONUP ;; Mu.mouse.right.down = false + elif msg == WM_MBUTTONDOWN ;; Mu.mouse.middle.down = true + elif msg == WM_MBUTTONUP ;; Mu.mouse.middle.down = false + elif msg == WM_MOUSEWHEEL + if wparam->int > 0 + Mu.mouse.wheel = 1 + else + Mu.mouse.wheel = -1 + // Case(WM_CHAR){ + // Utf32Result utf32 = Utf16ToUtf32((u16 *)&wparam, 1); + // if(!utf32.error){ + // int index = OS->input_count; + // OS->input_count = ClampTop(OS->input_count + 1, (int)(ArrayCount(OS->input) - 1)); + // OS->input[index] = utf32.out_str; + // } + // }Break; + + + else;; result = DefWindowProcW(hwnd, msg, wparam, lparam) + + return result + + +/*# +mapping = [ + ["Up", "VK_UP"], + ["Down", "VK_DOWN"], + ["Left", "VK_LEFT"], + ["Right", "VK_RIGHT"], + ["Escape", "VK_ESCAPE"], + ["Control", "VK_CONTROL"], + ["Backspace", "VK_BACK"], + ["Alt", "VK_MENU"], + ["Shift", "VK_SHIFT"], + ["Tab", "VK_TAB"], + ["F1", "VK_F1"], + ["F2", "VK_F2"], + ["F3", "VK_F3"], + ["F4", "VK_F4"], + ["F5", "VK_F5"], + ["F6", "VK_F6"], + ["F7", "VK_F7"], + ["F8", "VK_F8"], + ["F9", "VK_F9"], + ["F10", "VK_F10"], + ["F11", "VK_F11"], + ["F12", "VK_F12"], + ["A", "'A'"], + ["B", "'B'"], + ["C", "'C'"], + ["D", "'D'"], + ["E", "'E'"], + ["F", "'F'"], + ["G", "'G'"], + ["H", "'H'"], + ["I", "'I'"], + ["J", "'J'"], + ["K", "'K'"], + ["L", "'L'"], + ["M", "'M'"], + ["N", "'N'"], + ["O", "'O'"], + ["P", "'P'"], + ["Q", "'Q'"], + ["R", "'R'"], + ["S", "'S'"], + ["T", "'T'"], + ["U", "'U'"], + ["V", "'V'"], + ["W", "'W'"], + ["X", "'X'"], + ["Y", "'Y'"], + ["Z", "'Z'"], + ["K0", "'0'"], + ["K1", "'1'"], + ["K2", "'2'"], + ["K3", "'3'"], + ["K4", "'4'"], + ["K5", "'5'"], + ["K6", "'6'"], + ["K7", "'7'"], + ["K8", "'8'"], + ["K9", "'9'"], +] + + +print("MapVKToKey :: (vk: WPARAM): Key") +el = "" +for val,map in mapping: + print(f" {el}if vk == {map} ;; return Key.{val}") + el = "el" +print(" return Key.None") +*/ +MapVKToKey :: (vk: WPARAM): Key + if vk == VK_UP ;; return Key.Up + elif vk == VK_DOWN ;; return Key.Down + elif vk == VK_LEFT ;; return Key.Left + elif vk == VK_RIGHT ;; return Key.Right + elif vk == VK_ESCAPE ;; return Key.Escape + elif vk == VK_CONTROL ;; return Key.Control + elif vk == VK_BACK ;; return Key.Backspace + elif vk == VK_MENU ;; return Key.Alt + elif vk == VK_SHIFT ;; return Key.Shift + elif vk == VK_TAB ;; return Key.Tab + elif vk == VK_F1 ;; return Key.F1 + elif vk == VK_F2 ;; return Key.F2 + elif vk == VK_F3 ;; return Key.F3 + elif vk == VK_F4 ;; return Key.F4 + elif vk == VK_F5 ;; return Key.F5 + elif vk == VK_F6 ;; return Key.F6 + elif vk == VK_F7 ;; return Key.F7 + elif vk == VK_F8 ;; return Key.F8 + elif vk == VK_F9 ;; return Key.F9 + elif vk == VK_F10 ;; return Key.F10 + elif vk == VK_F11 ;; return Key.F11 + elif vk == VK_F12 ;; return Key.F12 + elif vk == 'A' ;; return Key.A + elif vk == 'B' ;; return Key.B + elif vk == 'C' ;; return Key.C + elif vk == 'D' ;; return Key.D + elif vk == 'E' ;; return Key.E + elif vk == 'F' ;; return Key.F + elif vk == 'G' ;; return Key.G + elif vk == 'H' ;; return Key.H + elif vk == 'I' ;; return Key.I + elif vk == 'J' ;; return Key.J + elif vk == 'K' ;; return Key.K + elif vk == 'L' ;; return Key.L + elif vk == 'M' ;; return Key.M + elif vk == 'N' ;; return Key.N + elif vk == 'O' ;; return Key.O + elif vk == 'P' ;; return Key.P + elif vk == 'Q' ;; return Key.Q + elif vk == 'R' ;; return Key.R + elif vk == 'S' ;; return Key.S + elif vk == 'T' ;; return Key.T + elif vk == 'U' ;; return Key.U + elif vk == 'V' ;; return Key.V + elif vk == 'W' ;; return Key.W + elif vk == 'X' ;; return Key.X + elif vk == 'Y' ;; return Key.Y + elif vk == 'Z' ;; return Key.Z + elif vk == '0' ;; return Key.K0 + elif vk == '1' ;; return Key.K1 + elif vk == '2' ;; return Key.K2 + elif vk == '3' ;; return Key.K3 + elif vk == '4' ;; return Key.K4 + elif vk == '5' ;; return Key.K5 + elif vk == '6' ;; return Key.K6 + elif vk == '7' ;; return Key.K7 + elif vk == '8' ;; return Key.K8 + elif vk == '9' ;; return Key.K9 + return Key.None /*END*/ \ No newline at end of file diff --git a/src/base/array.hpp b/src/base/array.hpp new file mode 100644 index 0000000..f33f6f1 --- /dev/null +++ b/src/base/array.hpp @@ -0,0 +1,306 @@ +// #define ARRAY_ALLOCATOR_TYPE Allocator + +#ifndef ARRAY_PRIVATE_FUNCTION + #if defined(__GNUC__) || defined(__clang__) + #define ARRAY_PRIVATE_FUNCTION __attribute__((unused)) static + #else + #define ARRAY_PRIVATE_FUNCTION static + #endif +#endif + +#ifndef ARRAY_ALLOCATE + #include + #define ARRAY_ALLOCATE(allocator, size) malloc(size) +#endif + +#ifndef ARRAY_DEALLOCATE + #include + #define ARRAY_DEALLOCATE(allocator, p) free(p) +#endif + +#ifndef ARRAY_ASSERT + #include + #define ARRAY_ASSERT(x) assert(x) +#endif + +#ifndef ARRAY_MemoryMove + #include + #define ARRAY_MemoryMove(dst, src, size) memmove(dst, src, size) +#endif + +#ifndef ARRAY_SET_DEFAULT_ALLOCATOR + #define ARRAY_SET_DEFAULT_ALLOCATOR +// Example: +// #define ARRAY_SET_DEFAULT_ALLOCATOR if (!allocator) allocator = global_heap; +#endif + +#ifdef DEFER_HEADER +#define ForArrayRemovable(a) for (int __i = 0; __i < (a).len; __i += 1) +#define ForArrayRemovablePrepare(a) \ + auto &it = (a)[__i]; \ + bool remove_it = false; \ + Defer { \ + if (remove_it) { \ + (a).ordered_remove(it); \ + __i -= 1; \ + } \ + } +#define ForArrayRemovableDeclare() (remove_it = true) +#endif + +#if !defined(ARRAY_ALLOCATOR_CODE) + #if defined(ARRAY_ALLOCATOR_TYPE) + #define ARRAY_ALLOCATOR_CODE(x) x + #define ARRAY_ALLOCATOR_PARAM ARRAY_ALLOCATOR_TYPE *allocator, + #define ARRAY_ALLOCATOR_PASS allocator, + #define ARRAY__PASS(ctx, name) (ctx)->name, + #else + #define ARRAY_ALLOCATOR_CODE(x) + #define ARRAY_ALLOCATOR_PARAM + #define ARRAY_ALLOCATOR_PASS + #define ARRAY__PASS(ctx, name) + #endif +#endif + +#if !defined(For) +#define For2(a,it) for(auto &it : (a)) +#define For(x) For2(x,it) +#endif + +template +struct Array { + ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator;) + T *data; + int cap, len; + + T &operator[](int index) { + ARRAY_ASSERT(index >= 0 && index < len); + return data[index]; + } + T &operator[](long long index) { + ARRAY_ASSERT(index >= 0 && index < len); + return data[index]; + } + + bool is_first(T &item) { return &item == first(); } + bool is_last(T &item) { return &item == last(); } + + bool contains(T *item) { + bool result = item >= data && item < data + len; + return result; + } + + int get_index(T &item) { + ARRAY_ASSERT((data <= &item) && ((data + len) > &item)); + size_t offset = &item - data; + return (int)offset; + } + + void add(T item) { + try_growing(); + data[len++] = item; + } + + // Struct needs to have 'value_to_sort_by' field + void sorted_insert_decreasing(T item) { + int insert_index = -1; + For(*this) { + if (it.value_to_sort_by <= item.value_to_sort_by) { + insert_index = get_index(it); + insert(item, insert_index); + break; + } + } + + if (insert_index == -1) { + add(item); + } + } + + void bounded_add(T item) { + ARRAY_ASSERT(len + 1 <= cap); + try_growing(); // in case of error + data[len++] = item; + } + + T *alloc(const T &item) { + try_growing(); + T *ref = data + len++; + *ref = item; + return ref; + } + + T *alloc() { + try_growing(); + T *ref = data + len++; + *ref = {}; + return ref; + } + + void add_array(T *items, int item_count) { + for (int i = 0; i < item_count; i += 1) { + add(items[i]); + } + } + + void reserve(int size) { + ARRAY_ASSERT(size == 0 || (size != 0 && size > cap)); + ARRAY_SET_DEFAULT_ALLOCATOR; + + void *p = ARRAY_ALLOCATE(allocator, size * sizeof(T)); + ARRAY_ASSERT(p); + + if (data) { + ARRAY_MemoryMove(p, data, len * sizeof(T)); + ARRAY_DEALLOCATE(allocator, data); + } + + data = (T *)p; + cap = size; + } + + void init(ARRAY_ALLOCATOR_PARAM int size) { + len = 0; + cap = 0; + data = 0; + ARRAY_ALLOCATOR_CODE(this->allocator = allocator;) + reserve(size); + } + + void reset() { + len = 0; + } + + T pop() { + ARRAY_ASSERT(len > 0); + return data[--len]; + } + + void unordered_remove(T &item) { // DONT USE IN LOOPS !!!! + ARRAY_ASSERT(len > 0); + ARRAY_ASSERT(&item >= begin() && &item < end()); + item = data[--len]; + } + + int get_index(const T &item) { + ptrdiff_t index = (ptrdiff_t)(&item - data); + ARRAY_ASSERT(index >= 0 && index < len); + // ARRAY_ASSERT(index > INT_MIN && index < INT_MAX); + return (int)index; + } + + void ordered_remove(T &item) { // DONT USE IN LOOPS !!! + ARRAY_ASSERT(len > 0); + ARRAY_ASSERT(&item >= begin() && &item < end()); + int index = get_index(item); + ARRAY_ASSERT(index >= 0 && index < len); + + int right_len = len - index - 1; + ARRAY_MemoryMove(data + index, data + index + 1, right_len * sizeof(T)); + len -= 1; + } + + void insert(T item, int index) { + if (index == len) { + add(item); + return; + } + + ARRAY_ASSERT(index < len); + ARRAY_ASSERT(index >= 0); + + try_growing(); + int right_len = len - index; + ARRAY_MemoryMove(data + index + 1, data + index, sizeof(T) * right_len); + data[index] = item; + len += 1; + } + + void dealloc() { + if (data) ARRAY_DEALLOCATE(allocator, data); + data = 0; + len = cap = 0; + } + + Array exact_copy(ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator)) { + Array result = {}; + ARRAY_ALLOCATOR_CODE(result.allocator = allocator;) + result.reserve(cap); + + ARRAY_MemoryMove(result.data, data, sizeof(T) * len); + result.len = len; + return result; + } + + Array tight_copy(ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator)) { + Array result = {}; + ARRAY_ALLOCATOR_CODE(result.allocator = allocator;) + result.reserve(len); + + ARRAY_MemoryMove(result.data, data, sizeof(T) * len); + result.len = len; + return result; + } + + T *first() { + ARRAY_ASSERT(len > 0); + return data; + } + T *last() { + ARRAY_ASSERT(len > 0); + return data + len - 1; + } + T *front() { + ARRAY_ASSERT(len > 0); + return data; + } + T *back() { + ARRAY_ASSERT(len > 0); + return data + len - 1; + } + T *begin() { return data; } + T *end() { return data + len; } + + struct Reverse_Iter { + T *data; + Array *arr; + + Reverse_Iter operator++(int) { + Reverse_Iter ret = *this; + data -= 1; + return ret; + } + Reverse_Iter &operator++() { + data -= 1; + return *this; + } + + T &operator*() { return data[0]; } + T *operator->() { return data; } + + friend bool operator==(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data == b.data; }; + friend bool operator!=(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data != b.data; }; + + Reverse_Iter begin() { return Reverse_Iter{arr->end() - 1, arr}; } + Reverse_Iter end() { return Reverse_Iter{arr->begin() - 1, arr}; } + }; + + Reverse_Iter reverse() { return {end() - 1, this}; } + + private: + void try_growing() { + if (len + 1 > cap) { + int new_size = cap * 2; + if (new_size < 16) new_size = 16; + + reserve(new_size); + } + } + + void try_growing_to_fit_item_count(int item_count) { + if (len + item_count > cap) { + int new_size = max(16, (cap + item_count) * 2); + reserve(new_size); + } + } +}; diff --git a/base.cpp b/src/base/base.cpp similarity index 92% rename from base.cpp rename to src/base/base.cpp index 742a567..06ea4be 100644 --- a/base.cpp +++ b/src/base/base.cpp @@ -1,347 +1,350 @@ -#define CORE_BASE - -#if defined(__clang__) - #define COMPILER_CLANG 1 - #if defined(_WIN32) - #define OS_WINDOWS 1 - #elif defined(__linux__) - #define OS_LINUX 1 - #else - #error Couldnt figure out the platform automatically - #endif -#elif defined(_MSC_VER) - #define COMPILER_MSVC 1 - #define OS_WINDOWS 1 -#elif defined(__GNUC__) - #define COMPILER_GCC 1 - #if defined(__linux__) - #define OS_LINUX 1 - #endif -#else - #error Couldnt figure out the compiler -#endif - -#if defined(OS_MAC) - #define OS_UNIX 1 -#endif -#if defined(OS_LINUX) - #define OS_UNIX 1 -#endif - -#if !defined(COMPILER_MSVC) - #define COMPILER_MSVC 0 -#endif -#if !defined(COMPILER_GCC) - #define COMPILER_GCC 0 -#endif -#if !defined(COMPILER_CLANG) - #define COMPILER_CLANG 0 -#endif -#if !defined(OS_WINDOWS) - #define OS_WINDOWS 0 -#endif -#if !defined(OS_LINUX) - #define OS_LINUX 0 -#endif -#if !defined(OS_MAC) - #define OS_MAC 0 -#endif -#if !defined(OS_UNIX) - #define OS_UNIX 0 -#endif - -#if OS_WINDOWS - #define OS_EXE ".exe" - #define OS_NAME "Win32"_s - #define OS_NAME_LOWER "win32"_s -#elif OS_LINUX - #define OS_EXE ".out" - #define OS_NAME "Linux"_s - #define OS_NAME_LOWER "linux"_s -#elif OS_MAC - #define OS_EXE ".out" - #define OS_NAME "Mac"_s - #define OS_NAME_LOWER "mac"_s -#else - #error Couldnt figure out the OS with C macros! -#endif - -#if OS_WINDOWS - #define NOMINMAX - #ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif - #include - #define Breakpoint __debugbreak() - #define force_inline __forceinline -#else - #define Breakpoint (*(volatile int *)0 = 0) - #define force_inline inline -#endif - -#include -#include -#include -typedef int8_t S8; -typedef int16_t S16; -typedef int32_t S32; -typedef int64_t S64; -typedef uint8_t U8; -typedef uint16_t U16; -typedef uint32_t U32; -typedef uint64_t U64; -typedef S8 B8; -typedef S16 B16; -typedef S32 B32; -typedef S64 B64; - -typedef float F32; -typedef double F64; - -#define U64MAX UINT64_MAX -#define U32MAX UINT32_MAX -#define U16MAX UINT16_MAX -#define U8MAX UINT8_MAX -#define U64MIN 0 -#define U32MIN 0 -#define U16MIN 0 -#define U8MIN 0 -#define S64MAX INT64_MAX -#define S64MIN INT64_MIN -#define S32MAX INT32_MAX -#define S32MIN INT32_MIN -#define S16MAX INT16_MAX -#define S16MIN INT16_MIN -#define S8MAX INT8_MAX -#define S8MIN INT8_MIN -#define F32MAX FLT_MAX -#define F32MIN FLT_MIN -#define F64MAX DBL_MAX -#define F64MIN DBL_MIN - -#include - -#define api -#define CORE_Static static -#define global static -#define assert(x) \ - do { \ - if (!(x)) Breakpoint; \ - } while (0) -#define assert_message(x, ...) \ - do { \ - if (!(x)) { \ - printf(__VA_ARGS__); \ - printf("\n"); \ - Breakpoint; \ - } \ - } while (0) -#define invalid_codepath assert_message(0, "Invalid codepath") -#define invalid_return \ - do { \ - assert_message(0, "Invalid codepath"); \ - return {}; \ - } while (0) -#define invalid_default_case \ - default: \ - invalid_codepath -#define not_implemented assert_message(0, "Not implemented") -#define unused(x) ((void)x) -#define buff_cap(x) (sizeof(x) / sizeof((x)[0])) -#define is_flag_set(val, flag) ((val) & (flag)) -#define set_flag(val, flag) ((val) |= (flag)) -#define unset_flag(val, flag) ((val) &= (~(flag))) -#define bit_flag(x) (1ull << (x)) -#define kib(x) ((x)*1024llu) -#define mib(x) (kib(x) * 1024llu) -#define gib(x) (mib(x) * 1024llu) -#define JOIN1(X, Y) X##Y -#define JOIN(X, Y) JOIN1(X, Y) -#define string_expand(x) (int)x.len, x.str - -struct String { - U8 *str; - S64 len; -}; -global String string_null = {(U8 *)"null", 4}; - -union Intern_String { // Basically just String - String s; - struct { - U8 *str; - S64 len; - }; -}; - -struct Allocator { - typedef void *Allocate(Allocator *, size_t); - typedef void Deallocate(Allocator *, void *p); - - Allocate *allocate; - Deallocate *deallocate; -}; - -CORE_Static void memory_zero(void *p, size_t size); -CORE_Static void deallocate_stub(Allocator *, void *) {} - -#define allocate_array(a, T, size, ...) (T *)allocate_size(a, sizeof(T) * (size), ##__VA_ARGS__) -#define allocate_struct(a, T, ...) allocate_array(a, T, 1, ##__VA_ARGS__) -CORE_Static void *allocate_size(Allocator *allocator, size_t size, bool zero_memory = true) { - void *result = allocator->allocate(allocator, size); - if (zero_memory) { - memory_zero(result, size); - } - return result; -} - -CORE_Static void deallocate(Allocator *allocator, void *p) { - assert(p); - allocator->deallocate(allocator, p); -} - -//----------------------------------------------------------------------------- -// Utilities -//----------------------------------------------------------------------------- -CORE_Static size_t -get_align_offset(size_t size, size_t align) { - size_t mask = align - 1; - size_t val = size & mask; - if (val) { - val = align - val; - } - return val; -} - -CORE_Static size_t -align_up(size_t size, size_t align) { - size_t result = size + get_align_offset(size, align); - return result; -} - -CORE_Static size_t -align_down(size_t size, size_t align) { - size += 1; // Make sure 8 when align is 8 doesn't get rounded down to 0 - size_t result = size - (align - get_align_offset(size, align)); - return result; -} - -CORE_Static void -memory_copy(void *dst, const void *src, size_t size) { - U8 *d = (U8 *)dst; - U8 *s = (U8 *)src; - for (size_t i = 0; i < size; i++) { - d[i] = s[i]; - } -} - -CORE_Static void -memory_zero(void *p, size_t size) { - U8 *pp = (U8 *)p; - for (size_t i = 0; i < size; i++) - pp[i] = 0; -} - -template -void swap(T &a, T &b) { - T temp = a; - a = b; - b = temp; -} - -template -T max(T a, T b) { - if (a > b) return a; - return b; -} - -template -T min(T a, T b) { - if (a > b) return b; - return a; -} - -template -T clamp_top(T val, T max) { - if (val > max) val = max; - return val; -} - -template -T clamp_bot(T bot, T val) { - if (val < bot) val = bot; - return val; -} - -template -T clamp(T min, T val, T max) { - if (val > max) val = max; - if (val < min) val = min; - return val; -} - -CORE_Static U64 -hash_string(String string) { - U64 hash = (U64)14695981039346656037ULL; - for (S64 i = 0; i < string.len; i++) { - hash = hash ^ (U64)(string.str[i]); - hash = hash * (U64)1099511628211ULL; - } - return hash; -} - -force_inline U64 -hash_u64(U64 x) { - U64 result = hash_string({(U8 *)&x, sizeof(x)}); - return result; -} - -force_inline U64 -hash_ptr(const void *ptr) { - return hash_u64((uintptr_t)ptr); -} - -CORE_Static U64 -hash_mix(U64 x, U64 y) { - // @note: murmur hash 3 mixer but I add the 'y' - // which means it's probably bad, hopefully better - // then some random scribble I could do - x ^= (y >> 33); - x *= 0xff51afd7ed558ccd; - x ^= (x >> 33); - x *= 0xc4ceb9fe1a85ec53; - x ^= (y >> 33); - return x; -} - -force_inline U64 -is_pow2(U64 x) { - assert(x != 0); - B32 result = (x & (x - 1llu)) == 0; - return result; -} - -force_inline U64 -wrap_around_pow2(U64 x, U64 power_of_2) { - assert(is_pow2(power_of_2)); - U64 r = (((x) & ((power_of_2)-1llu))); - return r; -} - -force_inline String -operator""_s(const char *str, size_t size) { - return String{(U8 *)str, (S64)size}; -} - -force_inline B32 -operator==(Intern_String a, Intern_String b) { - return a.str == b.str; -} - -force_inline B32 -operator!=(Intern_String a, Intern_String b) { - B32 result = a.str == b.str; - return !result; -} - -#define For_Linked_List_Named(a, it) for (auto *it = (a); it; it = it->next) -#define For_Linked_List(a) For_Linked_List_Named(a, it) +#define CORE_BASE + +#if defined(__clang__) + #define COMPILER_CLANG 1 + #if defined(_WIN32) + #define OS_WINDOWS 1 + #elif defined(__linux__) + #define OS_LINUX 1 + #else + #error Couldnt figure out the platform automatically + #endif +#elif defined(_MSC_VER) + #define COMPILER_MSVC 1 + #define OS_WINDOWS 1 +#elif defined(__GNUC__) + #define COMPILER_GCC 1 + #if defined(__linux__) + #define OS_LINUX 1 + #endif +#else + #error Couldnt figure out the compiler +#endif + +#if defined(OS_MAC) + #define OS_UNIX 1 +#endif +#if defined(OS_LINUX) + #define OS_UNIX 1 +#endif + +#if !defined(COMPILER_MSVC) + #define COMPILER_MSVC 0 +#endif +#if !defined(COMPILER_GCC) + #define COMPILER_GCC 0 +#endif +#if !defined(COMPILER_CLANG) + #define COMPILER_CLANG 0 +#endif +#if !defined(OS_WINDOWS) + #define OS_WINDOWS 0 +#endif +#if !defined(OS_LINUX) + #define OS_LINUX 0 +#endif +#if !defined(OS_MAC) + #define OS_MAC 0 +#endif +#if !defined(OS_UNIX) + #define OS_UNIX 0 +#endif + +#if OS_WINDOWS + #define OS_EXE ".exe" + #define OS_NAME "Win32"_s + #define OS_NAME_LOWER "win32"_s +#elif OS_LINUX + #define OS_EXE ".out" + #define OS_NAME "Linux"_s + #define OS_NAME_LOWER "linux"_s +#elif OS_MAC + #define OS_EXE ".out" + #define OS_NAME "Mac"_s + #define OS_NAME_LOWER "mac"_s +#else + #error Couldnt figure out the OS with C macros! +#endif + +#if OS_WINDOWS + #define NOMINMAX + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #include + #define Breakpoint __debugbreak() + #define force_inline __forceinline +#else + #define Breakpoint (*(volatile int *)0 = 0) + #define force_inline inline +#endif + +#include +#include +#include +typedef int8_t S8; +typedef int16_t S16; +typedef int32_t S32; +typedef int64_t S64; +typedef uint8_t U8; +typedef uint16_t U16; +typedef uint32_t U32; +typedef uint64_t U64; +typedef S8 B8; +typedef S16 B16; +typedef S32 B32; +typedef S64 B64; + +typedef float F32; +typedef double F64; + +#define U64MAX UINT64_MAX +#define U32MAX UINT32_MAX +#define U16MAX UINT16_MAX +#define U8MAX UINT8_MAX +#define U64MIN 0 +#define U32MIN 0 +#define U16MIN 0 +#define U8MIN 0 +#define S64MAX INT64_MAX +#define S64MIN INT64_MIN +#define S32MAX INT32_MAX +#define S32MIN INT32_MIN +#define S16MAX INT16_MAX +#define S16MIN INT16_MIN +#define S8MAX INT8_MAX +#define S8MIN INT8_MIN +#define F32MAX FLT_MAX +#define F32MIN FLT_MIN +#define F64MAX DBL_MAX +#define F64MIN DBL_MIN + +#include + +#define api +#define CORE_Static static +#define global static +#define assert(x) \ + do { \ + if (!(x)) { \ + printf("Assertion! %s", #x); \ + Breakpoint; \ + } \ + } while (0) +#define assert_message(x, ...) \ + do { \ + if (!(x)) { \ + printf(__VA_ARGS__); \ + printf("\n"); \ + Breakpoint; \ + } \ + } while (0) +#define invalid_codepath assert_message(0, "Invalid codepath") +#define invalid_return \ + do { \ + assert_message(0, "Invalid codepath"); \ + return {}; \ + } while (0) +#define invalid_default_case \ + default: \ + invalid_codepath +#define not_implemented assert_message(0, "Not implemented") +#define unused(x) ((void)x) +#define buff_cap(x) (sizeof(x) / sizeof((x)[0])) +#define is_flag_set(val, flag) ((val) & (flag)) +#define set_flag(val, flag) ((val) |= (flag)) +#define unset_flag(val, flag) ((val) &= (~(flag))) +#define bit_flag(x) (1ull << (x)) +#define kib(x) ((x)*1024llu) +#define mib(x) (kib(x) * 1024llu) +#define gib(x) (mib(x) * 1024llu) +#define JOIN1(X, Y) X##Y +#define JOIN(X, Y) JOIN1(X, Y) +#define string_expand(x) (int)x.len, x.str + +struct String { + U8 *str; + S64 len; +}; +global String string_null = {(U8 *)"null", 4}; + +union Intern_String { // Basically just String + String s; + struct { + U8 *str; + S64 len; + }; +}; + +struct Allocator { + typedef void *Allocate(Allocator *, size_t); + typedef void Deallocate(Allocator *, void *p); + + Allocate *allocate; + Deallocate *deallocate; +}; + +CORE_Static void memory_zero(void *p, size_t size); +CORE_Static void deallocate_stub(Allocator *, void *) {} + +#define allocate_array(a, T, size, ...) (T *)allocate_size(a, sizeof(T) * (size), ##__VA_ARGS__) +#define allocate_struct(a, T, ...) allocate_array(a, T, 1, ##__VA_ARGS__) +CORE_Static void *allocate_size(Allocator *allocator, size_t size, bool zero_memory = true) { + void *result = allocator->allocate(allocator, size); + if (zero_memory) { + memory_zero(result, size); + } + return result; +} + +CORE_Static void deallocate(Allocator *allocator, void *p) { + assert(p); + allocator->deallocate(allocator, p); +} + +//----------------------------------------------------------------------------- +// Utilities +//----------------------------------------------------------------------------- +CORE_Static size_t +get_align_offset(size_t size, size_t align) { + size_t mask = align - 1; + size_t val = size & mask; + if (val) { + val = align - val; + } + return val; +} + +CORE_Static size_t +align_up(size_t size, size_t align) { + size_t result = size + get_align_offset(size, align); + return result; +} + +CORE_Static size_t +align_down(size_t size, size_t align) { + size += 1; // Make sure 8 when align is 8 doesn't get rounded down to 0 + size_t result = size - (align - get_align_offset(size, align)); + return result; +} + +CORE_Static void +memory_copy(void *dst, const void *src, size_t size) { + U8 *d = (U8 *)dst; + U8 *s = (U8 *)src; + for (size_t i = 0; i < size; i++) { + d[i] = s[i]; + } +} + +CORE_Static void +memory_zero(void *p, size_t size) { + U8 *pp = (U8 *)p; + for (size_t i = 0; i < size; i++) + pp[i] = 0; +} + +template +void swap(T &a, T &b) { + T temp = a; + a = b; + b = temp; +} + +template +T max(T a, T b) { + if (a > b) return a; + return b; +} + +template +T min(T a, T b) { + if (a > b) return b; + return a; +} + +template +T clamp_top(T val, T max) { + if (val > max) val = max; + return val; +} + +template +T clamp_bot(T bot, T val) { + if (val < bot) val = bot; + return val; +} + +template +T clamp(T min, T val, T max) { + if (val > max) val = max; + if (val < min) val = min; + return val; +} + +CORE_Static U64 +hash_string(String string) { + U64 hash = (U64)14695981039346656037ULL; + for (S64 i = 0; i < string.len; i++) { + hash = hash ^ (U64)(string.str[i]); + hash = hash * (U64)1099511628211ULL; + } + return hash; +} + +force_inline U64 +hash_u64(U64 x) { + U64 result = hash_string({(U8 *)&x, sizeof(x)}); + return result; +} + +force_inline U64 +hash_ptr(const void *ptr) { + return hash_u64((uintptr_t)ptr); +} + +CORE_Static U64 +hash_mix(U64 x, U64 y) { + // @note: murmur hash 3 mixer but I add the 'y' + // which means it's probably bad, hopefully better + // then some random scribble I could do + x ^= (y >> 33); + x *= 0xff51afd7ed558ccd; + x ^= (x >> 33); + x *= 0xc4ceb9fe1a85ec53; + x ^= (y >> 33); + return x; +} + +force_inline U64 +is_pow2(U64 x) { + assert(x != 0); + B32 result = (x & (x - 1llu)) == 0; + return result; +} + +force_inline U64 +wrap_around_pow2(U64 x, U64 power_of_2) { + assert(is_pow2(power_of_2)); + U64 r = (((x) & ((power_of_2)-1llu))); + return r; +} + +force_inline String +operator""_s(const char *str, size_t size) { + return String{(U8 *)str, (S64)size}; +} + +force_inline B32 +operator==(Intern_String a, Intern_String b) { + return a.str == b.str; +} + +force_inline B32 +operator!=(Intern_String a, Intern_String b) { + B32 result = a.str == b.str; + return !result; +} + +#define For_Linked_List_Named(a, it) for (auto *it = (a); it; it = it->next) +#define For_Linked_List(a) For_Linked_List_Named(a, it) diff --git a/base_arena.cpp b/src/base/base_arena.cpp similarity index 52% rename from base_arena.cpp rename to src/base/base_arena.cpp index c7e4518..c22e47b 100644 --- a/base_arena.cpp +++ b/src/base/base_arena.cpp @@ -1,140 +1,147 @@ -constexpr size_t ARENA_BLOCK_SIZE = mib(4); -constexpr size_t ARENA_ALIGNMENT = 8; - -struct CRT_Heap : Allocator {}; -CORE_Static void *crt_allocate(Allocator *allocator, size_t size) { return malloc(size); } -CORE_Static void crt_deallocate(Allocator *allocator, void *p) { return free(p); } -CORE_Static CRT_Heap make_crt_heap() { - CRT_Heap result = {}; - result.allocate = crt_allocate; - result.deallocate = crt_deallocate; - return result; -} - -//----------------------------------------------------------------------------- -// Memory OS -//----------------------------------------------------------------------------- -struct OS_Memory { - size_t commit, reserve; - U8 *data; -}; -CORE_Static OS_Memory os_reserve(size_t size); -CORE_Static B32 os_commit(OS_Memory *m, size_t size); -CORE_Static void os_release(OS_Memory *m); -CORE_Static B32 os_decommit_pos(OS_Memory *m, size_t pos); - -//----------------------------------------------------------------------------- -// Memory arenas -//----------------------------------------------------------------------------- -global const size_t default_reserve_size = gib(4); -global const size_t default_alignment = 8; -global const size_t additional_commit_size = mib(1); -struct Arena : Allocator { - OS_Memory memory; - size_t alignment; - size_t len; - String debug_string; -}; - -CORE_Static void -arena_pop_pos(Arena *arena, size_t pos) { - pos = clamp_top(pos, arena->len); - arena->len = pos; -} - -CORE_Static void * -arena_pop(Arena *arena, size_t size) { - size = clamp_top(size, arena->len); - arena->len -= size; - return arena->memory.data + arena->len; -} - -CORE_Static void -arena_release(Arena *arena) { - os_release(&arena->memory); -} - -CORE_Static void -arena_clear(Arena *arena) { - arena_pop_pos(arena, 0); -} - -CORE_Static void * -arena_push_size(Arena *a, size_t size) { - size_t generous_size = size + a->alignment; - if (a->len + generous_size > a->memory.commit) { - assert(a->memory.reserve > 0); - B32 result = os_commit(&a->memory, generous_size + additional_commit_size); - assert(result); - } - - a->len = align_up(a->len, a->alignment); - assert(a->memory.reserve > a->len + size); - void *result = (U8 *)a->memory.data + a->len; - a->len += size; - - return result; -} - -#define push_struct_copy(a, T, p) (T *)push_copy(a, p, sizeof(T)) -CORE_Static void * -push_copy(Arena *a, void *p, size_t size) { - void *result = arena_push_size(a, size); - memory_copy(result, p, size); - return result; -} - -CORE_Static Arena -push_arena(Allocator *allocator, size_t size, String debug_name) { - Arena result = {}; - result.memory.data = (U8 *)allocate_size(allocator, size); - result.memory.reserve = size; - result.alignment = default_alignment; - result.debug_string = debug_name; - result.allocate = (Allocator::Allocate *)arena_push_size; - result.deallocate = (Allocator::Deallocate *)deallocate_stub; - return result; -} - -CORE_Static void -arena_init(Arena *a, String debug_name) { - a->memory = os_reserve(default_reserve_size); - a->alignment = default_alignment; - a->debug_string = debug_name; - a->allocate = (Allocator::Allocate *)arena_push_size; - a->deallocate = (Allocator::Deallocate *)deallocate_stub; -} - -CORE_Static Arena -arena_sub(Arena *base, size_t size, String debug_name) { - Arena result = {}; - result.memory.data = (U8 *)arena_push_size(base, size); - result.memory.commit = size; - result.memory.reserve = size; - result.alignment = default_alignment; - result.allocate = (Allocator::Allocate *)arena_push_size; - result.deallocate = (Allocator::Deallocate *)deallocate_stub; - return result; -} - -CORE_Static Arena -arena_from_buffer(void *buffer, size_t size) { - Arena result = {}; - result.memory.data = (U8 *)buffer; - result.memory.commit = size; - result.memory.reserve = size; - result.alignment = default_alignment; - result.allocate = (Allocator::Allocate *)arena_push_size; - result.deallocate = (Allocator::Deallocate *)deallocate_stub; - return result; -} - -struct Scoped_Arena { - Arena *arena; - size_t pos; - Scoped_Arena(Arena *arena) { - this->arena = arena; - this->pos = arena->len; - } - ~Scoped_Arena() { this->arena->len = this->pos; } -}; +constexpr size_t ARENA_BLOCK_SIZE = mib(4); +constexpr size_t ARENA_ALIGNMENT = 8; + +struct CRT_Heap : Allocator {}; +CORE_Static void *crt_allocate(Allocator *allocator, size_t size) { return malloc(size); } +CORE_Static void crt_deallocate(Allocator *allocator, void *p) { return free(p); } +CORE_Static CRT_Heap make_crt_heap() { + CRT_Heap result = {}; + result.allocate = crt_allocate; + result.deallocate = crt_deallocate; + return result; +} + +//----------------------------------------------------------------------------- +// Memory OS +//----------------------------------------------------------------------------- +struct OS_Memory { + size_t commit, reserve; + U8 *data; +}; +CORE_Static OS_Memory os_reserve(size_t size); +CORE_Static B32 os_commit(OS_Memory *m, size_t size); +CORE_Static void os_release(OS_Memory *m); +CORE_Static B32 os_decommit_pos(OS_Memory *m, size_t pos); + +//----------------------------------------------------------------------------- +// Memory arenas +//----------------------------------------------------------------------------- +global const size_t default_reserve_size = gib(4); +global const size_t default_alignment = 8; +global const size_t additional_commit_size = mib(1); +global U32 global_arena_ids; +struct Arena : Allocator { + OS_Memory memory; + size_t alignment; + size_t len; + String debug_string; + U32 id; +}; + +CORE_Static void +arena_pop_pos(Arena *arena, size_t pos) { + pos = clamp_top(pos, arena->len); + arena->len = pos; +} + +CORE_Static void * +arena_pop(Arena *arena, size_t size) { + size = clamp_top(size, arena->len); + arena->len -= size; + return arena->memory.data + arena->len; +} + +CORE_Static void +arena_release(Arena *arena) { + os_release(&arena->memory); +} + +CORE_Static void +arena_clear(Arena *arena) { + arena_pop_pos(arena, 0); +} + +static float GLOBAL_VIS_VALUE; +CORE_Static void * +arena_push_size(Arena *a, size_t size) { + size_t generous_size = size + a->alignment; + if (a->len + generous_size > a->memory.commit) { + assert(a->memory.reserve > 0); + B32 result = os_commit(&a->memory, generous_size + additional_commit_size); + assert(result); + } + + a->len = align_up(a->len, a->alignment); + assert(a->memory.reserve > a->len + size); + void *result = (U8 *)a->memory.data + a->len; + a->len += size; + + return result; +} + +#define push_struct_copy(a, T, p) (T *)push_copy(a, p, sizeof(T)) +CORE_Static void * +push_copy(Arena *a, void *p, size_t size) { + void *result = arena_push_size(a, size); + memory_copy(result, p, size); + return result; +} + +CORE_Static Arena +push_arena(Allocator *allocator, size_t size, String debug_name) { + Arena result = {}; + result.memory.data = (U8 *)allocate_size(allocator, size); + result.memory.reserve = size; + result.alignment = default_alignment; + result.id = ++global_arena_ids; + result.debug_string = debug_name; + result.allocate = (Allocator::Allocate *)arena_push_size; + result.deallocate = (Allocator::Deallocate *)deallocate_stub; + return result; +} + +CORE_Static void +arena_init(Arena *a, String debug_name) { + a->memory = os_reserve(default_reserve_size); + a->alignment = default_alignment; + a->id = ++global_arena_ids; + a->debug_string = debug_name; + a->allocate = (Allocator::Allocate *)arena_push_size; + a->deallocate = (Allocator::Deallocate *)deallocate_stub; +} + +CORE_Static Arena +arena_sub(Arena *base, size_t size, String debug_name) { + Arena result = {}; + result.memory.data = (U8 *)arena_push_size(base, size); + result.memory.commit = size; + result.memory.reserve = size; + result.alignment = default_alignment; + result.allocate = (Allocator::Allocate *)arena_push_size; + result.deallocate = (Allocator::Deallocate *)deallocate_stub; + result.id = ++global_arena_ids; + return result; +} + +CORE_Static Arena +arena_from_buffer(void *buffer, size_t size) { + Arena result = {}; + result.memory.data = (U8 *)buffer; + result.memory.commit = size; + result.memory.reserve = size; + result.alignment = default_alignment; + result.allocate = (Allocator::Allocate *)arena_push_size; + result.deallocate = (Allocator::Deallocate *)deallocate_stub; + result.id = ++global_arena_ids; + return result; +} + +struct Scoped_Arena { + Arena *arena; + size_t pos; + Scoped_Arena(Arena *arena) { + this->arena = arena; + this->pos = arena->len; + } + ~Scoped_Arena() { this->arena->len = this->pos; } +}; diff --git a/base_data_structures.cpp b/src/base/base_data_structures.cpp similarity index 81% rename from base_data_structures.cpp rename to src/base/base_data_structures.cpp index 834e20c..97e4a53 100644 --- a/base_data_structures.cpp +++ b/src/base/base_data_structures.cpp @@ -1,454 +1,452 @@ -#include "core/defer.hpp" -#define ARRAY_ALLOCATOR_TYPE Allocator -#define ARRAY_ASSERT assert -#define ARRAY_ALLOCATE(allocator, size) allocate_size(allocator, size) -#define ARRAY_DEALLOCATE(allocator, p) deallocate(allocator, p) -#include "core/array.hpp" -#include "core/linked_list.h" - -//----------------------------------------------------------------------------- -// Map -//----------------------------------------------------------------------------- -struct Map_Key_Value { - int occupied; - U64 key; - void *value; -}; - -struct Map { - Allocator *allocator; - Map_Key_Value *data; - S64 len; - S64 cap; -}; -CORE_Static void map_insert(Map *map, U64 key, void *val); - -CORE_Static void -map_grow(Map *map, S64 new_size) { - new_size = max((S64)16, new_size); - assert(new_size > map->cap); - assert(is_pow2(new_size)); - assert(map->allocator); - - Map new_map = {}; - new_map.data = allocate_array(map->allocator, Map_Key_Value, new_size); - new_map.cap = new_size; - new_map.allocator = map->allocator; - - for (S64 i = 0; i < map->cap; i++) { - if (map->data[i].occupied) { - map_insert(&new_map, map->data[i].key, map->data[i].value); - } - } - if (map->data) deallocate(map->allocator, map->data); - *map = new_map; -} - -CORE_Static Map -map_make(Allocator *a, S64 size) { - Map result = {a}; - map_grow(&result, size); - return result; -} - -CORE_Static void -map_insert(Map *map, U64 key, void *val) { - assert(val); - assert(key); - // if(key == 0) key+=1; - - if ((2 * map->len) + 1 > map->cap) { - map_grow(map, 2 * map->cap); - } - - U64 hash = hash_u64(key); - U64 index = wrap_around_pow2(hash, map->cap); - U64 i = index; - for (;;) { - if (map->data[i].occupied == false) { - map->len++; - map->data[i].occupied = true; - map->data[i].key = key; - map->data[i].value = val; - return; - } - else if (map->data[i].key == key) { - map->data[i].value = val; - return; - } - - i = wrap_around_pow2(i + 1, map->cap); - if (i == map->cap) { - return; - } - } -} - -CORE_Static Map_Key_Value * -map_base_get(Map *map, U64 key) { - if (map->len == 0) return 0; - assert(key); - - U64 hash = hash_u64(key); - U64 index = wrap_around_pow2(hash, map->cap); - U64 i = index; - for (;;) { - if (map->data[i].key == key) { - return map->data + i; - } - else if (map->data[i].key == 0) { - return 0; - } - - i = wrap_around_pow2(i + 1, map->cap); - if (i == map->cap) { - return 0; - } - } -} - -CORE_Static void * -map_get(Map *map, U64 key) { - Map_Key_Value *result = map_base_get(map, key); - if (result && result->occupied) return result->value; - return 0; -} - -CORE_Static void * -map_remove(Map *map, U64 key) { - Map_Key_Value *kv = map_base_get(map, key); - if (kv) { - kv->occupied = false; - return kv->value; - } - return 0; -} - -CORE_Static void * -map_get(Map *map, void *pointer) { - return map_get(map, (U64)pointer); -} - -CORE_Static void * -map_get(Map *map, Intern_String string) { - return map_get(map, hash_string(string.s)); -} - -CORE_Static void -map_insert(Map *map, void *key, void *value) { - map_insert(map, (U64)key, value); -} - -CORE_Static void -map_insert(Map *map, Intern_String key, void *value) { - map_insert(map, hash_string(key.s), value); -} - -//----------------------------------------------------------------------------- -// String intern -//----------------------------------------------------------------------------- -struct Intern_Table { - Allocator *string_allocator; - Map map; - U8 *first_keyword; - U8 *last_keyword; -}; - -CORE_Static Intern_Table -intern_table_make(Allocator *string_allocator, Allocator *map_allocator, S64 initial_size = 32) { - Intern_Table result = {}; - result.map = map_make(map_allocator, initial_size); - result.string_allocator = string_allocator; - return result; -} - -CORE_Static Intern_String -intern_string(Intern_Table *t, String string) { - assert(t->string_allocator); - U64 hash = hash_string(string); - U8 *slot = (U8 *)map_get(&t->map, hash); - if (slot) { - // @todo: Is this a cast bug: *(slot-sizeof(S64))? slot is u8 so truncates? - Intern_String result = { - {slot, *(slot - sizeof(S64))} - }; - return result; - } - - S64 *len_address = (S64 *)allocate_size(t->string_allocator, string.len + 1 + sizeof(S64), false); - *len_address = string.len; - - U8 *string_address = (U8 *)(len_address + 1); - memory_copy(string_address, string.str, string.len); - string_address[string.len] = 0; - - map_insert(&t->map, hash, string_address); - Intern_String result = { - {string_address, *len_address} - }; - - return result; -} - -//----------------------------------------------------------------------------- -// Array List -//----------------------------------------------------------------------------- -const int LIST_DEFAULT_BLOCK_SIZE = 16; -const int LIST_DEFAULT_ALLOCATION_MUL = 2; - -template -struct List_Node { - List_Node *next; - List_Node *prev; - int cap; - int len; - T data[]; -}; - -template -struct List { - int block_size = 0; - int allocation_multiplier = 0; - List_Node *first = 0; - List_Node *last = 0; - List_Node *first_free = 0; - - struct Iter { - T *item; - List_Node *node; - int node_index; - - T &operator*() { return *item; } - - Iter &operator++() { - if (node) { - if (node_index + 1 >= node->len) { - node = node->next; - node_index = -1; - item = 0; - } - - if (node) { - node_index += 1; - item = node->data + node_index; - } - } - return *this; - } - }; - - Iter begin() { - Iter result = {}; - result.node = first; - result.node_index = -1; - return ++result; - } - Iter end() { return {}; } - friend bool operator!=(Iter &a, Iter &b) { return a.item != b.item; } -}; - -template -List_Node *list_allocate_node(Allocator *arena, int size) { - auto node = (List_Node *)allocate_size(arena, sizeof(List_Node) + size * sizeof(T), false); - node->cap = size; - node->len = 0; - node->next = 0; - node->prev = 0; - return node; -} - -template -void list_allocate_free_node(Allocator *arena, List *list, int size) { - List_Node *node = list_allocate_node(arena, size); - DLL_STACK_ADD(list->first_free, node); -} - -template -void list_make_sure_there_is_room_for_item_count(Allocator *arena, List *list, int item_count) { - if (list->last == 0 || list->last->len + item_count > list->last->cap) { - // Not enough space we need to get a new block - List_Node *node = 0; - - // Iterate the free list to check if we have a block of required size there - For_Linked_List(list->first_free) { - if (it->cap >= item_count) { - DLL_STACK_REMOVE(list->first_free, it); - node = it; - node->len = 0; - break; - } - } - - // We don't have a block on the free list need to allocate - if (!node) { - // Set default values if not initialized - if (!list->allocation_multiplier) list->allocation_multiplier = LIST_DEFAULT_ALLOCATION_MUL; - if (!list->block_size) list->block_size = LIST_DEFAULT_BLOCK_SIZE; - - if (item_count > list->block_size) - list->block_size = item_count * 2; - node = list_allocate_node(arena, list->block_size); - list->block_size *= list->allocation_multiplier; - } - - assert(node); - DLL_QUEUE_ADD(list->first, list->last, node); - } -} - -template -T *list_get(List *list, int index, List_Node **node = 0, int *in_block_index = 0) { - if (list) { - int i = 0; - For_Linked_List(list->first) { - int lookup_i = index - i; - if (lookup_i < it->len) { - if (node) *node = it; - if (in_block_index) *in_block_index = lookup_i; - return it->data + lookup_i; - } - i += it->len; - } - } - return 0; -} - -template -T *getp(List *list, int index) { - return list_get(list, index); -} - -template -T get(List *list, int index) { - return *list_get(list, index); -} - -template -void add(Allocator *arena, List *list, T item) { - list_make_sure_there_is_room_for_item_count(arena, list, 1); - list->last->data[list->last->len++] = item; -} - -template -T *add_size(Allocator *arena, List *list, int count = 1, int zero_memory = 0) { - list_make_sure_there_is_room_for_item_count(arena, list, count); - T *result = list->last->data + list->last->len; - list->last->len += count; - - if (zero_memory) { - memory_zero(result, sizeof(T) * count); - } - - return result; -} - -template -void list_free_node(List *list, List_Node *node) { -#if 1 - // Make sure it's actually in list list - bool found = false; - For_Linked_List(list->first) { - if (it == node) { - found = true; - break; - } - } - assert(found); -#endif - - DLL_QUEUE_REMOVE(list->first, list->last, node); - DLL_STACK_ADD(list->first_free, node); -} - -template -int length(List *list) { - int result = 0; - For_Linked_List(list->first) { - result += it->len; - } - return result; -} - -template -void free_all_nodes(List *list) { - if (list->first == 0) return; - assert(!list->last->next); - assert(!list->first->prev); - list->last->next = list->first_free; - if (list->first_free) list->first_free->prev = list->last; - list->first_free = list->first; - list->last = list->first = 0; -} - -template -T ordered_remove(List *list, int index) { - List_Node *node; - int in_block_index; - T *data = list_get(list, index, &node, &in_block_index); - - assert_message(data, "Trying to unordered_remove element that's outside of the List"); - if (!data) - return {}; - - T result = *data; - - // Check if we need to deallocate the block - if (node->len == 1) { - list_free_node(list, node); - return result; - } - - // We need to move part of the block to fill the new empty spot - int right_count = (--node->len) - in_block_index; - memory_copy(data, data + 1, sizeof(T) * right_count); - return result; -} - -template -T unordered_remove(List *list, int index) { - List_Node *node; - T *data = list_get(list, index, &node); - - assert_message(data, "Trying to unordered_remove element that's outside of the List"); - if (!data) - return {}; - - assert(node->len); - assert(node->cap); - - // Swap - T result = *data; - *data = node->data[node->len - 1]; - - node->len -= 1; - if (node->len == 0) { - list_free_node(list, node); - } - - return result; -} - -template -T pop(List *list) { - assert(list->last != 0); - assert(list->last->len > 0); - T result = list->last->data[--list->last->len]; - if (list->last->len == 0) { - list_free_node(list, list->last); - } - return result; -} - -template -T *merge(Allocator *arena, List *list) { - int len = length(list); - T *result = allocate_size(arena, T, len, false); - - int i = 0; - For_Linked_List(list->first) { - memory_copy(result + i, it->data, it->len * sizeof(T)); - i += it->len; - } - - return result; -} +#include "../base/defer.hpp" +#define ARRAY_ALLOCATOR_TYPE Allocator +#define ARRAY_ASSERT assert +#define ARRAY_ALLOCATE(allocator, size) allocate_size(allocator, size) +#define ARRAY_DEALLOCATE(allocator, p) deallocate(allocator, p) +#include "../base/array.hpp" +#include "../base/linked_list.h" + +//----------------------------------------------------------------------------- +// Map +//----------------------------------------------------------------------------- +struct Map_Key_Value { + int occupied; + U64 key; + void *value; +}; + +struct Map { + Allocator *allocator; + Map_Key_Value *data; + S64 len; + S64 cap; +}; +CORE_Static void map_insert(Map *map, U64 key, void *val); + +CORE_Static void +map_grow(Map *map, S64 new_size) { + new_size = max((S64)16, new_size); + assert(new_size > map->cap); + assert(is_pow2(new_size)); + assert(map->allocator); + + Map new_map = {}; + new_map.data = allocate_array(map->allocator, Map_Key_Value, new_size); + new_map.cap = new_size; + new_map.allocator = map->allocator; + + for (S64 i = 0; i < map->cap; i++) { + if (map->data[i].occupied) { + map_insert(&new_map, map->data[i].key, map->data[i].value); + } + } + if (map->data) deallocate(map->allocator, map->data); + *map = new_map; +} + +CORE_Static Map +map_make(Allocator *a, S64 size) { + Map result = {a}; + map_grow(&result, size); + return result; +} + +CORE_Static void +map_insert(Map *map, U64 key, void *val) { + assert(val); + assert(key); + // if(key == 0) key+=1; + + if ((2 * map->len) + 1 > map->cap) { + map_grow(map, 2 * map->cap); + } + + U64 hash = hash_u64(key); + U64 index = wrap_around_pow2(hash, map->cap); + U64 i = index; + for (;;) { + if (map->data[i].occupied == false) { + map->len++; + map->data[i].occupied = true; + map->data[i].key = key; + map->data[i].value = val; + return; + } else if (map->data[i].key == key) { + map->data[i].value = val; + return; + } + + i = wrap_around_pow2(i + 1, map->cap); + if (i == map->cap) { + return; + } + } +} + +CORE_Static Map_Key_Value * +map_base_get(Map *map, U64 key) { + if (map->len == 0) return 0; + assert(key); + + U64 hash = hash_u64(key); + U64 index = wrap_around_pow2(hash, map->cap); + U64 i = index; + for (;;) { + if (map->data[i].key == key) { + return map->data + i; + } else if (map->data[i].key == 0) { + return 0; + } + + i = wrap_around_pow2(i + 1, map->cap); + if (i == map->cap) { + return 0; + } + } +} + +CORE_Static void * +map_get(Map *map, U64 key) { + Map_Key_Value *result = map_base_get(map, key); + if (result && result->occupied) return result->value; + return 0; +} + +CORE_Static void * +map_remove(Map *map, U64 key) { + Map_Key_Value *kv = map_base_get(map, key); + if (kv) { + kv->occupied = false; + return kv->value; + } + return 0; +} + +CORE_Static void * +map_get(Map *map, void *pointer) { + return map_get(map, (U64)pointer); +} + +CORE_Static void * +map_get(Map *map, Intern_String string) { + return map_get(map, hash_string(string.s)); +} + +CORE_Static void +map_insert(Map *map, void *key, void *value) { + map_insert(map, (U64)key, value); +} + +CORE_Static void +map_insert(Map *map, Intern_String key, void *value) { + map_insert(map, hash_string(key.s), value); +} + +//----------------------------------------------------------------------------- +// String intern +//----------------------------------------------------------------------------- +struct Intern_Table { + Allocator *string_allocator; + Map map; + U8 *first_keyword; + U8 *last_keyword; +}; + +CORE_Static Intern_Table +intern_table_make(Allocator *string_allocator, Allocator *map_allocator, S64 initial_size = 32) { + Intern_Table result = {}; + result.map = map_make(map_allocator, initial_size); + result.string_allocator = string_allocator; + return result; +} + +CORE_Static Intern_String +intern_string(Intern_Table *t, String string) { + assert(t->string_allocator); + U64 hash = hash_string(string); + U8 *slot = (U8 *)map_get(&t->map, hash); + if (slot) { + // @todo: Is this a cast bug: *(slot-sizeof(S64))? slot is u8 so truncates? + Intern_String result = { + {slot, *(slot - sizeof(S64))} + }; + return result; + } + + S64 *len_address = (S64 *)allocate_size(t->string_allocator, string.len + 1 + sizeof(S64), false); + *len_address = string.len; + + U8 *string_address = (U8 *)(len_address + 1); + memory_copy(string_address, string.str, string.len); + string_address[string.len] = 0; + + map_insert(&t->map, hash, string_address); + Intern_String result = { + {string_address, *len_address} + }; + + return result; +} + +//----------------------------------------------------------------------------- +// Array List +//----------------------------------------------------------------------------- +const int LIST_DEFAULT_BLOCK_SIZE = 16; +const int LIST_DEFAULT_ALLOCATION_MUL = 2; + +template +struct List_Node { + List_Node *next; + List_Node *prev; + int cap; + int len; + T data[]; +}; + +template +struct List { + int block_size = 0; + int allocation_multiplier = 0; + List_Node *first = 0; + List_Node *last = 0; + List_Node *first_free = 0; + + struct Iter { + T *item; + List_Node *node; + int node_index; + + T &operator*() { return *item; } + + Iter &operator++() { + if (node) { + if (node_index + 1 >= node->len) { + node = node->next; + node_index = -1; + item = 0; + } + + if (node) { + node_index += 1; + item = node->data + node_index; + } + } + return *this; + } + }; + + Iter begin() { + Iter result = {}; + result.node = first; + result.node_index = -1; + return ++result; + } + Iter end() { return {}; } + friend bool operator!=(Iter &a, Iter &b) { return a.item != b.item; } +}; + +template +List_Node *list_allocate_node(Allocator *arena, int size) { + auto node = (List_Node *)allocate_size(arena, sizeof(List_Node) + size * sizeof(T), false); + node->cap = size; + node->len = 0; + node->next = 0; + node->prev = 0; + return node; +} + +template +void list_allocate_free_node(Allocator *arena, List *list, int size) { + List_Node *node = list_allocate_node(arena, size); + DLL_STACK_ADD(list->first_free, node); +} + +template +void list_make_sure_there_is_room_for_item_count(Allocator *arena, List *list, int item_count) { + if (list->last == 0 || list->last->len + item_count > list->last->cap) { + // Not enough space we need to get a new block + List_Node *node = 0; + + // Iterate the free list to check if we have a block of required size there + For_Linked_List(list->first_free) { + if (it->cap >= item_count) { + DLL_STACK_REMOVE(list->first_free, it); + node = it; + node->len = 0; + break; + } + } + + // We don't have a block on the free list need to allocate + if (!node) { + // Set default values if not initialized + if (!list->allocation_multiplier) list->allocation_multiplier = LIST_DEFAULT_ALLOCATION_MUL; + if (!list->block_size) list->block_size = LIST_DEFAULT_BLOCK_SIZE; + + if (item_count > list->block_size) + list->block_size = item_count * 2; + node = list_allocate_node(arena, list->block_size); + list->block_size *= list->allocation_multiplier; + } + + assert(node); + DLL_QUEUE_ADD(list->first, list->last, node); + } +} + +template +T *list_get(List *list, int index, List_Node **node = 0, int *in_block_index = 0) { + if (list) { + int i = 0; + For_Linked_List(list->first) { + int lookup_i = index - i; + if (lookup_i < it->len) { + if (node) *node = it; + if (in_block_index) *in_block_index = lookup_i; + return it->data + lookup_i; + } + i += it->len; + } + } + return 0; +} + +template +T *getp(List *list, int index) { + return list_get(list, index); +} + +template +T get(List *list, int index) { + return *list_get(list, index); +} + +template +void add(Allocator *arena, List *list, T item) { + list_make_sure_there_is_room_for_item_count(arena, list, 1); + list->last->data[list->last->len++] = item; +} + +template +T *add_size(Allocator *arena, List *list, int count = 1, int zero_memory = 0) { + list_make_sure_there_is_room_for_item_count(arena, list, count); + T *result = list->last->data + list->last->len; + list->last->len += count; + + if (zero_memory) { + memory_zero(result, sizeof(T) * count); + } + + return result; +} + +template +void list_free_node(List *list, List_Node *node) { +#if 1 + // Make sure it's actually in list list + bool found = false; + For_Linked_List(list->first) { + if (it == node) { + found = true; + break; + } + } + assert(found); +#endif + + DLL_QUEUE_REMOVE(list->first, list->last, node); + DLL_STACK_ADD(list->first_free, node); +} + +template +int length(List *list) { + int result = 0; + For_Linked_List(list->first) { + result += it->len; + } + return result; +} + +template +void free_all_nodes(List *list) { + if (list->first == 0) return; + assert(!list->last->next); + assert(!list->first->prev); + list->last->next = list->first_free; + if (list->first_free) list->first_free->prev = list->last; + list->first_free = list->first; + list->last = list->first = 0; +} + +template +T ordered_remove(List *list, int index) { + List_Node *node; + int in_block_index; + T *data = list_get(list, index, &node, &in_block_index); + + assert_message(data, "Trying to unordered_remove element that's outside of the List"); + if (!data) + return {}; + + T result = *data; + + // Check if we need to deallocate the block + if (node->len == 1) { + list_free_node(list, node); + return result; + } + + // We need to move part of the block to fill the new empty spot + int right_count = (--node->len) - in_block_index; + memory_copy(data, data + 1, sizeof(T) * right_count); + return result; +} + +template +T unordered_remove(List *list, int index) { + List_Node *node; + T *data = list_get(list, index, &node); + + assert_message(data, "Trying to unordered_remove element that's outside of the List"); + if (!data) + return {}; + + assert(node->len); + assert(node->cap); + + // Swap + T result = *data; + *data = node->data[node->len - 1]; + + node->len -= 1; + if (node->len == 0) { + list_free_node(list, node); + } + + return result; +} + +template +T pop(List *list) { + assert(list->last != 0); + assert(list->last->len > 0); + T result = list->last->data[--list->last->len]; + if (list->last->len == 0) { + list_free_node(list, list->last); + } + return result; +} + +template +T *merge(Allocator *arena, List *list) { + int len = length(list); + T *result = allocate_size(arena, T, len, false); + + int i = 0; + For_Linked_List(list->first) { + memory_copy(result + i, it->data, it->len * sizeof(T)); + i += it->len; + } + + return result; +} diff --git a/base_string.cpp b/src/base/base_string.cpp similarity index 96% rename from base_string.cpp rename to src/base/base_string.cpp index 6dc723c..f416b6b 100644 --- a/base_string.cpp +++ b/src/base/base_string.cpp @@ -1,478 +1,478 @@ - -CORE_Static U8 -to_lower_case(U8 a) { - if (a >= 'A' && a <= 'Z') - a += 32; - return a; -} - -CORE_Static U8 -to_upper_case(U8 a) { - if (a >= 'a' && a <= 'z') - a -= 32; - return a; -} - -CORE_Static U8 -char_to_lower(U8 c) { - if (c >= 'A' && c <= 'Z') - c += 32; - return c; -} - -CORE_Static U8 -char_to_upper(U8 c) { - if (c >= 'a' && c <= 'z') - c -= 32; - return c; -} - -CORE_Static B32 -is_whitespace(U8 w) { - bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; - return result; -} - -CORE_Static B32 -is_alphabetic(U8 a) { - if ((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z')) { - return true; - } - return false; -} - -CORE_Static B32 -is_number(U8 a) { - B32 result = a >= '0' && a <= '9'; - return result; -} - -CORE_Static B32 -is_alphanumeric(U8 a) { - B32 result = is_number(a) || is_alphabetic(a); - return result; -} - -CORE_Static S64 -string_len(char *string) { - S64 len = 0; - while (*string++ != 0) len++; - return len; -} - -CORE_Static String -string_from_cstring(char *string) { - String result; - result.str = (U8 *)string; - result.len = string_len(string); - return result; -} - -CORE_Static B32 -string_compare(String a, String b, B32 ignore_case = false) { - if (a.len != b.len) - return false; - for (S64 i = 0; i < a.len; i++) { - U8 A = a.str[i]; - U8 B = b.str[i]; - if (ignore_case) { - A = to_lower_case(A); - B = to_lower_case(B); - } - if (A != B) - return false; - } - return true; -} - -CORE_Static B32 -cstring_compare(char *a, char *b, B32 ignore_case = false) { - return string_compare(string_from_cstring(a), string_from_cstring(b), ignore_case); -} - -CORE_Static B32 -operator==(String a, String b) { - return string_compare(a, b); -} - -CORE_Static String -string_copy(Allocator *a, String string) { - U8 *copy = allocate_array(a, U8, string.len + 1); - memory_copy(copy, string.str, string.len); - copy[string.len] = 0; - return String{copy, string.len}; -} - -CORE_Static String -string_fmtv(Allocator *a, const char *str, va_list args1) { - va_list args2; - va_copy(args2, args1); - S64 len = stbsp_vsnprintf(0, 0, str, args2); - va_end(args2); - - char *result = allocate_array(a, char, len + 1); - stbsp_vsnprintf(result, (int)(len + 1), str, args1); - - String res = {(U8 *)result, len}; - return res; -} - -#define STRING_FMT(alloc, str, result) \ - va_list args1; \ - va_start(args1, str); \ - String result = string_fmtv(alloc, str, args1); \ - va_end(args1) - -CORE_Static String -string_fmt(Allocator *a, const char *str, ...) { - STRING_FMT(a, str, result); - return result; -} - -//----------------------------------------------------------------------------- -// String builder -//----------------------------------------------------------------------------- -struct String_Builder_Block { - String_Builder_Block *next; - S64 cap; - S64 len; - U8 data[0]; -}; - -struct String_Builder { - Allocator *allocator; - String_Builder_Block *first_free; - String_Builder_Block *first; - String_Builder_Block *last; - U64 di; - - void reset() { - for (; first;) { - auto *block = first; - first = first->next; - - block->next = first_free; - first_free = block; - } - - last = 0; - assert(!last && !first); - } - - void push_block(size_t size) { - String_Builder_Block *block = 0; - if (first_free) { - block = first_free; - first_free = first_free->next; - } - else { - block = (String_Builder_Block *)allocate_size(allocator, sizeof(String_Builder_Block) + size, false); - } - memory_zero(block, sizeof(String_Builder_Block) + 1); // Also clear first byte of character data - block->cap = size; - SLL_QUEUE_ADD(first, last, block); - } - - void init(S64 size = 4096) { - assert(allocator); - push_block(size); - } - - void append_data(void *data, S64 size) { - if (first == 0) { - init(); - } - - S64 remaining_cap = last->cap - last->len; - if (size > remaining_cap) { - S64 new_block_size = max(last->cap * 2, size * 2); - push_block(new_block_size); - } - - U8 *write_address = last->data + last->len; - last->len += size; - memory_copy(write_address, data, size); - } - - void addf(const char *str, ...) { - if (first == 0) { - init(); - } - va_list args, args2; - va_start(args, str); - retry : { - String_Builder_Block *block = last; - S64 block_size = block->cap - block->len; - char *write_address = (char *)block->data + block->len; - - va_copy(args2, args); - int written = stbsp_vsnprintf(write_address, (int)block_size, str, args2); - va_end(args2); - - if (written > (block_size - 1)) { - S64 new_block_size = max(4096, (written + 1) * 2); - push_block(new_block_size); - goto retry; - } - block->len += written; - } - va_end(args); - di++; - } -}; - -CORE_Static String_Builder -string_builder_make(Allocator *a, S64 first_block_size = 4096) { - String_Builder sb = {a}; - sb.init(first_block_size); - return sb; -} - -// @! Make string_flatten a method -static String string_flatten(Allocator *a, String_Builder *b) { - // @Note(Krzosa): Compute size to allocate - S64 size = 1; - For_Linked_List(b->first) { - size += it->len; - } - - String result = {}; - result.str = (U8 *)allocate_size(a, size, false); - - // @Note(Krzosa): Copy the content of each block into the string - For_Linked_List(b->first) { - memory_copy(result.str + result.len, it->data, it->len); - result.len += it->len; - } - - result.str[result.len] = 0; - return result; -} - -//----------------------------------------------------------------------------- -// String ops -//----------------------------------------------------------------------------- -CORE_Static void -string_path_normalize(String s) { - for (S64 i = 0; i < s.len; i++) { - if (s.str[i] == '\\') - s.str[i] = '/'; - } -} - -CORE_Static String -string_make(char *str, S64 len) { - String result; - result.str = (U8 *)str; - result.len = len; - return result; -} - -CORE_Static String -string_make(U8 *str, S64 len) { - return string_make((char *)str, len); -} - -CORE_Static String -string_chop(String string, S64 len) { - len = clamp_top(len, string.len); - String result = string_make(string.str, string.len - len); - return result; -} - -CORE_Static String -string_skip(String string, S64 len) { - len = clamp_top(len, string.len); - S64 remain = string.len - len; - String result = string_make(string.str + len, remain); - return result; -} - -CORE_Static String -string_get_postfix(String string, S64 len) { - len = clamp_top(len, string.len); - S64 remain_len = string.len - len; - String result = string_make(string.str + remain_len, len); - return result; -} - -CORE_Static String -string_get_prefix(String string, S64 len) { - len = clamp_top(len, string.len); - String result = string_make(string.str, len); - return result; -} - -CORE_Static String -string_slice(String string, S64 first_index, S64 one_past_last_index) { - assert_message(first_index < one_past_last_index, "string_slice, first_index is bigger then one_past_last_index"); - assert_message(string.len > 0, "Slicing string of length 0! Might be an error!"); - String result = string; - if (string.len > 0) { - if (one_past_last_index > first_index) { - first_index = clamp_top(first_index, string.len - 1); - one_past_last_index = clamp_top(one_past_last_index, string.len); - result.str += first_index; - result.len = one_past_last_index - first_index; - } - else { - result.len = 0; - } - } - return result; -} - -CORE_Static String -string_trim(String string) { - if (string.len == 0) return string; - - S64 whitespace_begin = 0; - for (; whitespace_begin < string.len; whitespace_begin++) { - if (!is_whitespace(string.str[whitespace_begin])) { - break; - } - } - - S64 whitespace_end = string.len; - for (; whitespace_end != whitespace_begin; whitespace_end--) { - if (!is_whitespace(string.str[whitespace_end - 1])) { - break; - } - } - - if (whitespace_begin == whitespace_end) { - string.len = 0; - } - else { - string = string_slice(string, whitespace_begin, whitespace_end); - } - - return string; -} - -CORE_Static String -string_trim_end(String string) { - S64 whitespace_end = string.len; - for (; whitespace_end != 0; whitespace_end--) { - if (!is_whitespace(string.str[whitespace_end - 1])) { - break; - } - } - - String result = string_get_prefix(string, whitespace_end); - return result; -} - -CORE_Static String -string_to_lower_case(Allocator *arena, String s) { - String copy = string_copy(arena, s); - for (S64 i = 0; i < copy.len; i++) { - copy.str[i] = to_lower_case(copy.str[i]); - } - return copy; -} - -CORE_Static String -string_to_upper_case(Allocator *arena, String s) { - String copy = string_copy(arena, s); - for (S64 i = 0; i < copy.len; i++) { - copy.str[i] = to_upper_case(copy.str[i]); - } - return copy; -} - -typedef U32 MatchFlag; -enum { - MatchFlag_None = 0, - MatchFlag_FindLast = 1, - MatchFlag_IgnoreCase = 2, -}; - -CORE_Static B32 -string_find(String string, String find, MatchFlag flags, S64 *index_out) { - B32 result = false; - if (flags & MatchFlag_FindLast) { - for (S64 i = string.len; i != 0; i--) { - S64 index = i - 1; - String substring = string_slice(string, index, index + find.len); - if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) { - if (index_out) - *index_out = index; - result = true; - break; - } - } - } - else { - for (S64 i = 0; i < string.len; i++) { - String substring = string_slice(string, i, i + find.len); - if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) { - if (index_out) - *index_out = i; - result = true; - break; - } - } - } - - return result; -} - -CORE_Static String -string_chop_last_slash(String s) { - String result = s; - string_find(s, "/"_s, MatchFlag_FindLast, &result.len); - return result; -} - -CORE_Static String -string_chop_last_period(String s) { - String result = s; - string_find(s, "."_s, MatchFlag_FindLast, &result.len); - return result; -} - -CORE_Static String -string_skip_to_last_slash(String s) { - S64 pos; - String result = s; - if (string_find(s, "/"_s, MatchFlag_FindLast, &pos)) { - result = string_skip(result, pos + 1); - } - return result; -} - -CORE_Static String -string_skip_to_last_period(String s) { - S64 pos; - String result = s; - if (string_find(s, "."_s, MatchFlag_FindLast, &pos)) { - result = string_skip(result, pos + 1); - } - return result; -} - -struct String_Replace { - String find; - String replace; -}; - -CORE_Static String -string_replace(Arena *scratch, Allocator *allocator, String string, Array pairs) { - Scoped_Arena _scope(scratch); - String_Builder builder = {scratch}; - for (S64 i = 0; i < string.len; i++) { - For(pairs) { - String current = string_skip(string, i); - current = string_get_prefix(current, it.find.len); - if (current == it.find) { - builder.append_data(it.replace.str, it.replace.len); - i += it.find.len; - continue; - } - } - - builder.append_data(string.str + i, 1); - } - return string_flatten(allocator, &builder); + +CORE_Static U8 +to_lower_case(U8 a) { + if (a >= 'A' && a <= 'Z') + a += 32; + return a; +} + +CORE_Static U8 +to_upper_case(U8 a) { + if (a >= 'a' && a <= 'z') + a -= 32; + return a; +} + +CORE_Static U8 +char_to_lower(U8 c) { + if (c >= 'A' && c <= 'Z') + c += 32; + return c; +} + +CORE_Static U8 +char_to_upper(U8 c) { + if (c >= 'a' && c <= 'z') + c -= 32; + return c; +} + +CORE_Static B32 +is_whitespace(U8 w) { + bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; + return result; +} + +CORE_Static B32 +is_alphabetic(U8 a) { + if ((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z')) { + return true; + } + return false; +} + +CORE_Static B32 +is_number(U8 a) { + B32 result = a >= '0' && a <= '9'; + return result; +} + +CORE_Static B32 +is_alphanumeric(U8 a) { + B32 result = is_number(a) || is_alphabetic(a); + return result; +} + +CORE_Static S64 +string_len(char *string) { + S64 len = 0; + while (*string++ != 0) len++; + return len; +} + +CORE_Static String +string_from_cstring(char *string) { + String result; + result.str = (U8 *)string; + result.len = string_len(string); + return result; +} + +CORE_Static B32 +string_compare(String a, String b, B32 ignore_case = false) { + if (a.len != b.len) + return false; + for (S64 i = 0; i < a.len; i++) { + U8 A = a.str[i]; + U8 B = b.str[i]; + if (ignore_case) { + A = to_lower_case(A); + B = to_lower_case(B); + } + if (A != B) + return false; + } + return true; +} + +CORE_Static B32 +cstring_compare(char *a, char *b, B32 ignore_case = false) { + return string_compare(string_from_cstring(a), string_from_cstring(b), ignore_case); +} + +CORE_Static B32 +operator==(String a, String b) { + return string_compare(a, b); +} + +CORE_Static String +string_copy(Allocator *a, String string) { + U8 *copy = allocate_array(a, U8, string.len + 1); + memory_copy(copy, string.str, string.len); + copy[string.len] = 0; + return String{copy, string.len}; +} + +CORE_Static String +string_fmtv(Allocator *a, const char *str, va_list args1) { + va_list args2; + va_copy(args2, args1); + S64 len = stbsp_vsnprintf(0, 0, str, args2); + va_end(args2); + + char *result = allocate_array(a, char, len + 1); + stbsp_vsnprintf(result, (int)(len + 1), str, args1); + + String res = {(U8 *)result, len}; + return res; +} + +#define STRING_FMT(alloc, str, result) \ + va_list args1; \ + va_start(args1, str); \ + String result = string_fmtv(alloc, str, args1); \ + va_end(args1) + +CORE_Static String +string_fmt(Allocator *a, const char *str, ...) { + STRING_FMT(a, str, result); + return result; +} + +//----------------------------------------------------------------------------- +// String builder +//----------------------------------------------------------------------------- +struct String_Builder_Block { + String_Builder_Block *next; + S64 cap; + S64 len; + U8 data[0]; +}; + +struct String_Builder { + Allocator *allocator; + String_Builder_Block *first_free; + String_Builder_Block *first; + String_Builder_Block *last; + U64 di; + + void reset() { + for (; first;) { + auto *block = first; + first = first->next; + + block->next = first_free; + first_free = block; + } + + last = 0; + assert(!last && !first); + } + + void push_block(size_t size) { + String_Builder_Block *block = 0; + if (first_free) { + block = first_free; + first_free = first_free->next; + } + else { + block = (String_Builder_Block *)allocate_size(allocator, sizeof(String_Builder_Block) + size, false); + } + memory_zero(block, sizeof(String_Builder_Block) + 1); // Also clear first byte of character data + block->cap = size; + SLL_QUEUE_ADD(first, last, block); + } + + void init(S64 size = 4096) { + assert(allocator); + push_block(size); + } + + void append_data(void *data, S64 size) { + if (first == 0) { + init(); + } + + S64 remaining_cap = last->cap - last->len; + if (size > remaining_cap) { + S64 new_block_size = max(last->cap * 2, size * 2); + push_block(new_block_size); + } + + U8 *write_address = last->data + last->len; + last->len += size; + memory_copy(write_address, data, size); + } + + void addf(const char *str, ...) { + if (first == 0) { + init(); + } + va_list args, args2; + va_start(args, str); + retry : { + String_Builder_Block *block = last; + S64 block_size = block->cap - block->len; + char *write_address = (char *)block->data + block->len; + + va_copy(args2, args); + int written = stbsp_vsnprintf(write_address, (int)block_size, str, args2); + va_end(args2); + + if (written > (block_size - 1)) { + S64 new_block_size = max(4096, (written + 1) * 2); + push_block(new_block_size); + goto retry; + } + block->len += written; + } + va_end(args); + di++; + } +}; + +CORE_Static String_Builder +string_builder_make(Allocator *a, S64 first_block_size = 4096) { + String_Builder sb = {a}; + sb.init(first_block_size); + return sb; +} + +// @! Make string_flatten a method +static String string_flatten(Allocator *a, String_Builder *b) { + // @Note(Krzosa): Compute size to allocate + S64 size = 1; + For_Linked_List(b->first) { + size += it->len; + } + + String result = {}; + result.str = (U8 *)allocate_size(a, size, false); + + // @Note(Krzosa): Copy the content of each block into the string + For_Linked_List(b->first) { + memory_copy(result.str + result.len, it->data, it->len); + result.len += it->len; + } + + result.str[result.len] = 0; + return result; +} + +//----------------------------------------------------------------------------- +// String ops +//----------------------------------------------------------------------------- +CORE_Static void +string_path_normalize(String s) { + for (S64 i = 0; i < s.len; i++) { + if (s.str[i] == '\\') + s.str[i] = '/'; + } +} + +CORE_Static String +string_make(char *str, S64 len) { + String result; + result.str = (U8 *)str; + result.len = len; + return result; +} + +CORE_Static String +string_make(U8 *str, S64 len) { + return string_make((char *)str, len); +} + +CORE_Static String +string_chop(String string, S64 len) { + len = clamp_top(len, string.len); + String result = string_make(string.str, string.len - len); + return result; +} + +CORE_Static String +string_skip(String string, S64 len) { + len = clamp_top(len, string.len); + S64 remain = string.len - len; + String result = string_make(string.str + len, remain); + return result; +} + +CORE_Static String +string_get_postfix(String string, S64 len) { + len = clamp_top(len, string.len); + S64 remain_len = string.len - len; + String result = string_make(string.str + remain_len, len); + return result; +} + +CORE_Static String +string_get_prefix(String string, S64 len) { + len = clamp_top(len, string.len); + String result = string_make(string.str, len); + return result; +} + +CORE_Static String +string_slice(String string, S64 first_index, S64 one_past_last_index) { + assert_message(first_index < one_past_last_index, "string_slice, first_index is bigger then one_past_last_index"); + assert_message(string.len > 0, "Slicing string of length 0! Might be an error!"); + String result = string; + if (string.len > 0) { + if (one_past_last_index > first_index) { + first_index = clamp_top(first_index, string.len - 1); + one_past_last_index = clamp_top(one_past_last_index, string.len); + result.str += first_index; + result.len = one_past_last_index - first_index; + } + else { + result.len = 0; + } + } + return result; +} + +CORE_Static String +string_trim(String string) { + if (string.len == 0) return string; + + S64 whitespace_begin = 0; + for (; whitespace_begin < string.len; whitespace_begin++) { + if (!is_whitespace(string.str[whitespace_begin])) { + break; + } + } + + S64 whitespace_end = string.len; + for (; whitespace_end != whitespace_begin; whitespace_end--) { + if (!is_whitespace(string.str[whitespace_end - 1])) { + break; + } + } + + if (whitespace_begin == whitespace_end) { + string.len = 0; + } + else { + string = string_slice(string, whitespace_begin, whitespace_end); + } + + return string; +} + +CORE_Static String +string_trim_end(String string) { + S64 whitespace_end = string.len; + for (; whitespace_end != 0; whitespace_end--) { + if (!is_whitespace(string.str[whitespace_end - 1])) { + break; + } + } + + String result = string_get_prefix(string, whitespace_end); + return result; +} + +CORE_Static String +string_to_lower_case(Allocator *arena, String s) { + String copy = string_copy(arena, s); + for (S64 i = 0; i < copy.len; i++) { + copy.str[i] = to_lower_case(copy.str[i]); + } + return copy; +} + +CORE_Static String +string_to_upper_case(Allocator *arena, String s) { + String copy = string_copy(arena, s); + for (S64 i = 0; i < copy.len; i++) { + copy.str[i] = to_upper_case(copy.str[i]); + } + return copy; +} + +typedef U32 MatchFlag; +enum { + MatchFlag_None = 0, + MatchFlag_FindLast = 1, + MatchFlag_IgnoreCase = 2, +}; + +CORE_Static B32 +string_find(String string, String find, MatchFlag flags, S64 *index_out) { + B32 result = false; + if (flags & MatchFlag_FindLast) { + for (S64 i = string.len; i != 0; i--) { + S64 index = i - 1; + String substring = string_slice(string, index, index + find.len); + if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) { + if (index_out) + *index_out = index; + result = true; + break; + } + } + } + else { + for (S64 i = 0; i < string.len; i++) { + String substring = string_slice(string, i, i + find.len); + if (string_compare(substring, find, flags & MatchFlag_IgnoreCase)) { + if (index_out) + *index_out = i; + result = true; + break; + } + } + } + + return result; +} + +CORE_Static String +string_chop_last_slash(String s) { + String result = s; + string_find(s, "/"_s, MatchFlag_FindLast, &result.len); + return result; +} + +CORE_Static String +string_chop_last_period(String s) { + String result = s; + string_find(s, "."_s, MatchFlag_FindLast, &result.len); + return result; +} + +CORE_Static String +string_skip_to_last_slash(String s) { + S64 pos; + String result = s; + if (string_find(s, "/"_s, MatchFlag_FindLast, &pos)) { + result = string_skip(result, pos + 1); + } + return result; +} + +CORE_Static String +string_skip_to_last_period(String s) { + S64 pos; + String result = s; + if (string_find(s, "."_s, MatchFlag_FindLast, &pos)) { + result = string_skip(result, pos + 1); + } + return result; +} + +struct String_Replace { + String find; + String replace; +}; + +CORE_Static String +string_replace(Arena *scratch, Allocator *allocator, String string, Array pairs) { + Scoped_Arena _scope(scratch); + String_Builder builder = {scratch}; + for (S64 i = 0; i < string.len; i++) { + For(pairs) { + String current = string_skip(string, i); + current = string_get_prefix(current, it.find.len); + if (current == it.find) { + builder.append_data(it.replace.str, it.replace.len); + i += it.find.len; + continue; + } + } + + builder.append_data(string.str + i, 1); + } + return string_flatten(allocator, &builder); } \ No newline at end of file diff --git a/base_unicode.cpp b/src/base/base_unicode.cpp similarity index 96% rename from base_unicode.cpp rename to src/base/base_unicode.cpp index 41a17d7..45514ef 100644 --- a/base_unicode.cpp +++ b/src/base/base_unicode.cpp @@ -1,274 +1,274 @@ -global U32 question_mark32 = '?'; -global U16 question_mark16 = 0x003f; -global U8 question_mark8 = '?'; - -struct String32 { - U32 *str; - S64 len; -}; - -struct UTF32_Result { - U32 out_str; - S64 advance; - B32 error; -}; - -CORE_Static UTF32_Result -utf8_to_utf32(U8 *c, S64 max_advance) { - UTF32_Result result = {}; - - if ((c[0] & 0b10000000) == 0) { // Check if leftmost zero of first byte is unset - if (max_advance >= 1) { - result.out_str = c[0]; - result.advance = 1; - } - else result.error = 1; - } - - else if ((c[0] & 0b11100000) == 0b11000000) { - if ((c[1] & 0b11000000) == 0b10000000) { // Continuation byte required - if (max_advance >= 2) { - result.out_str = (U32)(c[0] & 0b00011111) << 6u | (c[1] & 0b00111111); - result.advance = 2; - } - else result.error = 2; - } - else result.error = 2; - } - - else if ((c[0] & 0b11110000) == 0b11100000) { - if ((c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000) { // Two continuation bytes required - if (max_advance >= 3) { - result.out_str = (U32)(c[0] & 0b00001111) << 12u | (U32)(c[1] & 0b00111111) << 6u | (c[2] & 0b00111111); - result.advance = 3; - } - else result.error = 3; - } - else result.error = 3; - } - - else if ((c[0] & 0b11111000) == 0b11110000) { - if ((c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000 && (c[3] & 0b11000000) == 0b10000000) { // Three continuation bytes required - if (max_advance >= 4) { - result.out_str = (U32)(c[0] & 0b00001111) << 18u | (U32)(c[1] & 0b00111111) << 12u | (U32)(c[2] & 0b00111111) << 6u | (U32)(c[3] & 0b00111111); - result.advance = 4; - } - else result.error = 4; - } - else result.error = 4; - } - else result.error = 4; - - return result; -} - -struct String16 { - U16 *str; - S64 len; -}; - -struct UTF16_Result { - U16 out_str[2]; - S32 len; - B32 error; -}; - -CORE_Static UTF16_Result -utf32_to_utf16(U32 codepoint) { - UTF16_Result result = {}; - if (codepoint < 0x10000) { - result.out_str[0] = (U16)codepoint; - result.out_str[1] = 0; - result.len = 1; - } - else if (codepoint <= 0x10FFFF) { - U32 code = (codepoint - 0x10000); - result.out_str[0] = (U16)(0xD800 | (code >> 10)); - result.out_str[1] = (U16)(0xDC00 | (code & 0x3FF)); - result.len = 2; - } - else { - result.error = 1; - } - - return result; -} - -struct UTF8_Result { - U8 out_str[4]; - S32 len; - B32 error; -}; - -CORE_Static UTF8_Result -utf32_to_utf8(U32 codepoint) { - UTF8_Result result = {}; - if (codepoint <= 0x7F) { - result.len = 1; - result.out_str[0] = (U8)codepoint; - } - else if (codepoint <= 0x7FF) { - result.len = 2; - result.out_str[0] = 0b11000000 | (0b00011111 & (codepoint >> 6)); - result.out_str[1] = 0b10000000 | (0b00111111 & codepoint); - } - else if (codepoint <= 0xFFFF) { // 16 bit word - result.len = 3; - result.out_str[0] = 0b11100000 | (0b00001111 & (codepoint >> 12)); // 4 bits - result.out_str[1] = 0b10000000 | (0b00111111 & (codepoint >> 6)); // 6 bits - result.out_str[2] = 0b10000000 | (0b00111111 & codepoint); // 6 bits - } - else if (codepoint <= 0x10FFFF) { // 21 bit word - result.len = 4; - result.out_str[0] = 0b11110000 | (0b00000111 & (codepoint >> 18)); // 3 bits - result.out_str[1] = 0b10000000 | (0b00111111 & (codepoint >> 12)); // 6 bits - result.out_str[2] = 0b10000000 | (0b00111111 & (codepoint >> 6)); // 6 bits - result.out_str[3] = 0b10000000 | (0b00111111 & codepoint); // 6 bits - } - else { - result.error = true; - } - - return result; -} - -CORE_Static UTF32_Result -utf16_to_utf32(U16 *c, S32 max_advance) { - UTF32_Result result = {}; - if (max_advance >= 1) { - result.advance = 1; - result.out_str = c[0]; - if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) { - if (max_advance >= 2) { - result.out_str = 0x10000; - result.out_str += (U32)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF); - result.advance = 2; - } - else result.error = 2; - } - } - else result.error = 1; - - return result; -} - -#define unicode_error(question_mark) \ - { \ - result.str[result.len++] = question_mark; \ - break; \ - } - -CORE_Static String32 -string16_to_string32(Allocator *allocator, String16 string) { - String32 result = {allocate_array(allocator, U32, string.len + 1)}; - for (S64 i = 0; i < string.len;) { - UTF32_Result decode = utf16_to_utf32(string.str + i, (S32)(string.len - i)); - if (!decode.error) { - i += decode.advance; - result.str[result.len++] = decode.out_str; - } - else unicode_error(question_mark32); - } - - result.str[result.len] = 0; - return result; -} - -CORE_Static String32 -string8_to_string32(Allocator *allocator, String string) { - String32 result = {allocate_array(allocator, U32, string.len + 1)}; - for (S64 i = 0; i < string.len;) { - UTF32_Result decode = utf8_to_utf32(string.str + i, string.len - i); - if (!decode.error) { - i += decode.advance; - result.str[result.len++] = decode.out_str; - } - else unicode_error(question_mark32); - } - result.str[result.len] = 0; - return result; -} - -CORE_Static String16 -string8_to_string16(Allocator *allocator, String in) { - String16 result = {allocate_array(allocator, U16, (in.len * 2) + 1)}; // @Note(Krzosa): Should be more then enough space - for (S64 i = 0; i < in.len;) { - UTF32_Result decode = utf8_to_utf32(in.str + i, in.len - i); - if (!decode.error) { - i += decode.advance; - UTF16_Result encode = utf32_to_utf16(decode.out_str); - if (!encode.error) { - for (S32 j = 0; j < encode.len; j++) { - result.str[result.len++] = encode.out_str[j]; - } - } - else unicode_error(question_mark16); - } - else unicode_error(question_mark16); - } - - result.str[result.len] = 0; - return result; -} - -CORE_Static String -string16_to_string8(Allocator *allocator, String16 in) { - String result = {allocate_array(allocator, U8, in.len * 4 + 1)}; - for (S64 i = 0; i < in.len;) { - UTF32_Result decode = utf16_to_utf32(in.str + i, (S32)(in.len - i)); - if (!decode.error) { - i += decode.advance; - UTF8_Result encode = utf32_to_utf8(decode.out_str); - if (!encode.error) { - for (S32 j = 0; j < encode.len; j++) - result.str[result.len++] = encode.out_str[j]; - } - else unicode_error(question_mark8); - } - else unicode_error(question_mark8); - } - - result.str[result.len] = 0; - return result; -} - -CORE_Static B32 -string_compare(String16 a, String16 b) { - if (a.len != b.len) return false; - for (S64 i = 0; i < a.len; i++) { - if (a.str[i] != b.str[i]) return false; - } - return true; -} - -CORE_Static B32 -string_compare(String32 a, String32 b) { - if (a.len != b.len) return false; - for (S64 i = 0; i < a.len; i++) { - if (a.str[i] != b.str[i]) return false; - } - return true; -} - -CORE_Static S64 -widechar_len(wchar_t *string) { - S64 len = 0; - while (*string++ != 0) len++; - return len; -} - -CORE_Static String16 -string16_from_widechar(wchar_t *string) { - String16 result; - result.str = (U16 *)string; - result.len = widechar_len(string); - return result; -} - -CORE_Static String -string16_copy(Allocator *a, String string) { - U8 *copy = allocate_array(a, U8, string.len + 1); - memory_copy(copy, string.str, string.len); - copy[string.len] = 0; - return String{copy, string.len}; -} +global U32 question_mark32 = '?'; +global U16 question_mark16 = 0x003f; +global U8 question_mark8 = '?'; + +struct String32 { + U32 *str; + S64 len; +}; + +struct UTF32_Result { + U32 out_str; + S64 advance; + B32 error; +}; + +CORE_Static UTF32_Result +utf8_to_utf32(U8 *c, S64 max_advance) { + UTF32_Result result = {}; + + if ((c[0] & 0b10000000) == 0) { // Check if leftmost zero of first byte is unset + if (max_advance >= 1) { + result.out_str = c[0]; + result.advance = 1; + } + else result.error = 1; + } + + else if ((c[0] & 0b11100000) == 0b11000000) { + if ((c[1] & 0b11000000) == 0b10000000) { // Continuation byte required + if (max_advance >= 2) { + result.out_str = (U32)(c[0] & 0b00011111) << 6u | (c[1] & 0b00111111); + result.advance = 2; + } + else result.error = 2; + } + else result.error = 2; + } + + else if ((c[0] & 0b11110000) == 0b11100000) { + if ((c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000) { // Two continuation bytes required + if (max_advance >= 3) { + result.out_str = (U32)(c[0] & 0b00001111) << 12u | (U32)(c[1] & 0b00111111) << 6u | (c[2] & 0b00111111); + result.advance = 3; + } + else result.error = 3; + } + else result.error = 3; + } + + else if ((c[0] & 0b11111000) == 0b11110000) { + if ((c[1] & 0b11000000) == 0b10000000 && (c[2] & 0b11000000) == 0b10000000 && (c[3] & 0b11000000) == 0b10000000) { // Three continuation bytes required + if (max_advance >= 4) { + result.out_str = (U32)(c[0] & 0b00001111) << 18u | (U32)(c[1] & 0b00111111) << 12u | (U32)(c[2] & 0b00111111) << 6u | (U32)(c[3] & 0b00111111); + result.advance = 4; + } + else result.error = 4; + } + else result.error = 4; + } + else result.error = 4; + + return result; +} + +struct String16 { + U16 *str; + S64 len; +}; + +struct UTF16_Result { + U16 out_str[2]; + S32 len; + B32 error; +}; + +CORE_Static UTF16_Result +utf32_to_utf16(U32 codepoint) { + UTF16_Result result = {}; + if (codepoint < 0x10000) { + result.out_str[0] = (U16)codepoint; + result.out_str[1] = 0; + result.len = 1; + } + else if (codepoint <= 0x10FFFF) { + U32 code = (codepoint - 0x10000); + result.out_str[0] = (U16)(0xD800 | (code >> 10)); + result.out_str[1] = (U16)(0xDC00 | (code & 0x3FF)); + result.len = 2; + } + else { + result.error = 1; + } + + return result; +} + +struct UTF8_Result { + U8 out_str[4]; + S32 len; + B32 error; +}; + +CORE_Static UTF8_Result +utf32_to_utf8(U32 codepoint) { + UTF8_Result result = {}; + if (codepoint <= 0x7F) { + result.len = 1; + result.out_str[0] = (U8)codepoint; + } + else if (codepoint <= 0x7FF) { + result.len = 2; + result.out_str[0] = 0b11000000 | (0b00011111 & (codepoint >> 6)); + result.out_str[1] = 0b10000000 | (0b00111111 & codepoint); + } + else if (codepoint <= 0xFFFF) { // 16 bit word + result.len = 3; + result.out_str[0] = 0b11100000 | (0b00001111 & (codepoint >> 12)); // 4 bits + result.out_str[1] = 0b10000000 | (0b00111111 & (codepoint >> 6)); // 6 bits + result.out_str[2] = 0b10000000 | (0b00111111 & codepoint); // 6 bits + } + else if (codepoint <= 0x10FFFF) { // 21 bit word + result.len = 4; + result.out_str[0] = 0b11110000 | (0b00000111 & (codepoint >> 18)); // 3 bits + result.out_str[1] = 0b10000000 | (0b00111111 & (codepoint >> 12)); // 6 bits + result.out_str[2] = 0b10000000 | (0b00111111 & (codepoint >> 6)); // 6 bits + result.out_str[3] = 0b10000000 | (0b00111111 & codepoint); // 6 bits + } + else { + result.error = true; + } + + return result; +} + +CORE_Static UTF32_Result +utf16_to_utf32(U16 *c, S32 max_advance) { + UTF32_Result result = {}; + if (max_advance >= 1) { + result.advance = 1; + result.out_str = c[0]; + if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) { + if (max_advance >= 2) { + result.out_str = 0x10000; + result.out_str += (U32)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF); + result.advance = 2; + } + else result.error = 2; + } + } + else result.error = 1; + + return result; +} + +#define unicode_error(question_mark) \ + { \ + result.str[result.len++] = question_mark; \ + break; \ + } + +CORE_Static String32 +string16_to_string32(Allocator *allocator, String16 string) { + String32 result = {allocate_array(allocator, U32, string.len + 1)}; + for (S64 i = 0; i < string.len;) { + UTF32_Result decode = utf16_to_utf32(string.str + i, (S32)(string.len - i)); + if (!decode.error) { + i += decode.advance; + result.str[result.len++] = decode.out_str; + } + else unicode_error(question_mark32); + } + + result.str[result.len] = 0; + return result; +} + +CORE_Static String32 +string8_to_string32(Allocator *allocator, String string) { + String32 result = {allocate_array(allocator, U32, string.len + 1)}; + for (S64 i = 0; i < string.len;) { + UTF32_Result decode = utf8_to_utf32(string.str + i, string.len - i); + if (!decode.error) { + i += decode.advance; + result.str[result.len++] = decode.out_str; + } + else unicode_error(question_mark32); + } + result.str[result.len] = 0; + return result; +} + +CORE_Static String16 +string8_to_string16(Allocator *allocator, String in) { + String16 result = {allocate_array(allocator, U16, (in.len * 2) + 1)}; // @Note(Krzosa): Should be more then enough space + for (S64 i = 0; i < in.len;) { + UTF32_Result decode = utf8_to_utf32(in.str + i, in.len - i); + if (!decode.error) { + i += decode.advance; + UTF16_Result encode = utf32_to_utf16(decode.out_str); + if (!encode.error) { + for (S32 j = 0; j < encode.len; j++) { + result.str[result.len++] = encode.out_str[j]; + } + } + else unicode_error(question_mark16); + } + else unicode_error(question_mark16); + } + + result.str[result.len] = 0; + return result; +} + +CORE_Static String +string16_to_string8(Allocator *allocator, String16 in) { + String result = {allocate_array(allocator, U8, in.len * 4 + 1)}; + for (S64 i = 0; i < in.len;) { + UTF32_Result decode = utf16_to_utf32(in.str + i, (S32)(in.len - i)); + if (!decode.error) { + i += decode.advance; + UTF8_Result encode = utf32_to_utf8(decode.out_str); + if (!encode.error) { + for (S32 j = 0; j < encode.len; j++) + result.str[result.len++] = encode.out_str[j]; + } + else unicode_error(question_mark8); + } + else unicode_error(question_mark8); + } + + result.str[result.len] = 0; + return result; +} + +CORE_Static B32 +string_compare(String16 a, String16 b) { + if (a.len != b.len) return false; + for (S64 i = 0; i < a.len; i++) { + if (a.str[i] != b.str[i]) return false; + } + return true; +} + +CORE_Static B32 +string_compare(String32 a, String32 b) { + if (a.len != b.len) return false; + for (S64 i = 0; i < a.len; i++) { + if (a.str[i] != b.str[i]) return false; + } + return true; +} + +CORE_Static S64 +widechar_len(wchar_t *string) { + S64 len = 0; + while (*string++ != 0) len++; + return len; +} + +CORE_Static String16 +string16_from_widechar(wchar_t *string) { + String16 result; + result.str = (U16 *)string; + result.len = widechar_len(string); + return result; +} + +CORE_Static String +string16_copy(Allocator *a, String string) { + U8 *copy = allocate_array(a, U8, string.len + 1); + memory_copy(copy, string.str, string.len); + copy[string.len] = 0; + return String{copy, string.len}; +} diff --git a/src/base/defer.hpp b/src/base/defer.hpp new file mode 100644 index 0000000..dc9693f --- /dev/null +++ b/src/base/defer.hpp @@ -0,0 +1,21 @@ +#define DEFER_HEADER +template +struct DEFER_ExitScope { + T lambda; + DEFER_ExitScope(T lambda) : lambda(lambda) {} + ~DEFER_ExitScope() { lambda(); } + DEFER_ExitScope(const DEFER_ExitScope &i) : lambda(i.lambda){}; + + private: + DEFER_ExitScope &operator=(const DEFER_ExitScope &); +}; + +class DEFER_ExitScopeHelp { + public: + template + DEFER_ExitScope operator+(T t) { return t; } +}; + +#define DEFER_CONCAT_INTERNAL(x, y) x##y +#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y) +#define Defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]() diff --git a/src/base/linked_list.h b/src/base/linked_list.h new file mode 100644 index 0000000..4456aed --- /dev/null +++ b/src/base/linked_list.h @@ -0,0 +1,131 @@ +#ifndef LINKED_LIST_HEADER +#define LINKED_LIST_HEADER + +#define GET_ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0]))) + +#define SLL_QUEUE_ADD_MOD(f, l, n, next) \ + do { \ + (n)->next = 0; \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } \ + else { \ + (l) = (l)->next = (n); \ + } \ + } while (0) +#define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next) + +#define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \ + do { \ + if ((f) == (l)) { \ + (f) = (l) = 0; \ + } \ + else { \ + (f) = (f)->next; \ + } \ + } while (0) +#define SLL_QUEUE_POP_FIRST(f, l) SLL_QUEUE_POP_FIRST_MOD(f, l, next) + +#define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \ + do { \ + (new_stack_base)->next = (stack_base); \ + (stack_base) = (new_stack_base); \ + } while (0) +#define SLL_STACK_ADD(stack_base, new_stack_base) \ + SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) + +#define SLL_STACK_POP_AND_STORE(stack_base, out_node) \ + do { \ + if (stack_base) { \ + (out_node) = (stack_base); \ + (stack_base) = (stack_base)->next; \ + (out_node)->next = 0; \ + } \ + } while (0) + +#define DLL_QUEUE_ADD_MOD(f, l, node, next, prev) \ + do { \ + if ((f) == 0) { \ + (f) = (l) = (node); \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + else { \ + (l)->next = (node); \ + (node)->prev = (l); \ + (node)->next = 0; \ + (l) = (node); \ + } \ + } while (0) +#define DLL_QUEUE_ADD(f, l, node) DLL_QUEUE_ADD_MOD(f, l, node, next, prev) +#define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \ + do { \ + if ((first) == (last)) { \ + IO_Assertf((node) == (first), "Not you are trying to remove is not in the list"); \ + (first) = (last) = 0; \ + } \ + else if ((last) == (node)) { \ + (last) = (last)->prev; \ + (last)->next = 0; \ + } \ + else if ((first) == (node)) { \ + (first) = (first)->next; \ + (first)->prev = 0; \ + } \ + else { \ + (node)->prev->next = (node)->next; \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) +#define DLL_QUEUE_REMOVE(first, last, node) DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) + +#define DLL_STACK_ADD_MOD(first, node, next, prev) \ + do { \ + (node)->next = (first); \ + if ((first)) \ + (first)->prev = (node); \ + (first) = (node); \ + (node)->prev = 0; \ + } while (0) +#define DLL_STACK_ADD(first, node) DLL_STACK_ADD_MOD(first, node, next, prev) +#define DLL_STACK_REMOVE_MOD(first, node, next, prev) \ + do { \ + if ((node) == (first)) { \ + (first) = (first)->next; \ + if ((first)) \ + (first)->prev = 0; \ + } \ + else { \ + (node)->prev->next = (node)->next; \ + if ((node)->next) \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) +#define DLL_STACK_REMOVE(first, node) DLL_STACK_REMOVE_MOD(first, node, next, prev) + +#define DLL_INSERT_NEXT_MOD(base, new, next, prev) \ + do { \ + if ((base) == 0) { \ + (base) = (new); \ + (new)->next = 0; \ + (new)->prev = 0; \ + } \ + else { \ + (new)->next = (base)->next; \ + (base)->next = (new); \ + (new)->prev = (base); \ + if ((new)->next) (new)->next->prev = (new); \ + } \ + } while (0) +#define DLL_INSERT_NEXT(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev) +#define DLL_INSERT_PREV(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev) + +#endif \ No newline at end of file diff --git a/stb_sprintf.h b/src/base/stb_sprintf.h similarity index 97% rename from stb_sprintf.h rename to src/base/stb_sprintf.h index 4ddc601..a86b351 100644 --- a/stb_sprintf.h +++ b/src/base/stb_sprintf.h @@ -1,1925 +1,1925 @@ -// stb_sprintf - v1.10 - public domain snprintf() implementation -// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 -// http://github.com/nothings/stb -// -// allowed types: sc uidBboXx p AaGgEef n -// lengths : hh h ll j z t I64 I32 I -// -// Contributors: -// Fabian "ryg" Giesen (reformatting) -// github:aganm (attribute format) -// -// Contributors (bugfixes): -// github:d26435 -// github:trex78 -// github:account-login -// Jari Komppa (SI suffixes) -// Rohit Nirmal -// Marcin Wojdyr -// Leonard Ritter -// Stefano Zanotti -// Adam Allison -// Arvid Gerstmann -// Markus Kolb -// -// LICENSE: -// -// See end of file for license information. - -#ifndef STB_SPRINTF_H_INCLUDE - #define STB_SPRINTF_H_INCLUDE - -/* -Single file sprintf replacement. - -Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. -Hereby placed in public domain. - -This is a full sprintf replacement that supports everything that -the C runtime sprintfs support, including float/double, 64-bit integers, -hex floats, field parameters (%*.*d stuff), length reads backs, etc. - -Why would you need this if sprintf already exists? Well, first off, -it's *much* faster (see below). It's also much smaller than the CRT -versions code-space-wise. We've also added some simple improvements -that are super handy (commas in thousands, callbacks at buffer full, -for example). Finally, the format strings for MSVC and GCC differ -for 64-bit integers (among other small things), so this lets you use -the same format strings in cross platform code. - -It uses the standard single file trick of being both the header file -and the source itself. If you just include it normally, you just get -the header file function definitions. To get the code, you include -it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. - -It only uses va_args macros from the C runtime to do it's work. It -does cast doubles to S64s and shifts and divides U64s, which does -drag in CRT code on most platforms. - -It compiles to roughly 8K with float support, and 4K without. -As a comparison, when using MSVC static libs, calling sprintf drags -in 16K. - -API: -==== -int stbsp_sprintf( char * buf, char const * fmt, ... ) -int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) - Convert an arg list into a buffer. stbsp_snprintf always returns - a zero-terminated string (unlike regular snprintf). - -int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) -int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) - Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns - a zero-terminated string (unlike regular snprintf). - -int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) - typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); - Convert into a buffer, calling back every STB_SPRINTF_MIN chars. - Your callback can then copy the chars out, print them or whatever. - This function is actually the workhorse for everything else. - The buffer you pass in must hold at least STB_SPRINTF_MIN characters. - // you return the next buffer to use or 0 to stop converting - -void stbsp_set_separators( char comma, char period ) - Set the comma and period characters to use. - -FLOATS/DOUBLES: -=============== -This code uses a internal float->ascii conversion method that uses -doubles with error correction (double-doubles, for ~105 bits of -precision). This conversion is round-trip perfect - that is, an atof -of the values output here will give you the bit-exact double back. - -One difference is that our insignificant digits will be different than -with MSVC or GCC (but they don't match each other either). We also -don't attempt to find the minimum length matching float (pre-MSVC15 -doesn't either). - -If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT -and you'll save 4K of code space. - -64-BIT INTS: -============ -This library also supports 64-bit integers and you can use MSVC style or -GCC style indicators (%I64d or %lld). It supports the C99 specifiers -for size_t and ptr_diff_t (%jd %zd) as well. - -EXTRAS: -======= -Like some GCCs, for integers and floats, you can use a ' (single quote) -specifier and commas will be inserted on the thousands: "%'d" on 12345 -would print 12,345. - -For integers and floats, you can use a "$" specifier and the number -will be converted to float and then divided to get kilo, mega, giga or -tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is -"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn -2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three -$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the -suffix, add "_" specifier: "%_$d" -> "2.53M". - -In addition to octal and hexadecimal conversions, you can print -integers in binary: "%b" for 256 would print 100. - -PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): -=================================================================== -"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) -"%24d" across all 32-bit ints (4.5x/4.2x faster) -"%x" across all 32-bit ints (4.5x/3.8x faster) -"%08x" across all 32-bit ints (4.3x/3.8x faster) -"%f" across e-10 to e+10 floats (7.3x/6.0x faster) -"%e" across e-10 to e+10 floats (8.1x/6.0x faster) -"%g" across e-10 to e+10 floats (10.0x/7.1x faster) -"%f" for values near e-300 (7.9x/6.5x faster) -"%f" for values near e+300 (10.0x/9.1x faster) -"%e" for values near e-300 (10.1x/7.0x faster) -"%e" for values near e+300 (9.2x/6.0x faster) -"%.320f" for values near e-300 (12.6x/11.2x faster) -"%a" for random values (8.6x/4.3x faster) -"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) -"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) -"%s%s%s" for 64 char strings (7.1x/7.3x faster) -"...512 char string..." ( 35.0x/32.5x faster!) -*/ - - #if defined(__clang__) - #if defined(__has_feature) && defined(__has_attribute) - #if __has_feature(address_sanitizer) - #if __has_attribute(__no_sanitize__) - #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) - #elif __has_attribute(__no_sanitize_address__) - #define STBSP__ASAN __attribute__((__no_sanitize_address__)) - #elif __has_attribute(__no_address_safety_analysis__) - #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) - #endif - #endif - #endif - #elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ - #define STBSP__ASAN __attribute__((__no_sanitize_address__)) - #endif - #endif - - #ifndef STBSP__ASAN - #define STBSP__ASAN - #endif - - #ifdef STB_SPRINTF_STATIC - #define STBSP__PUBLICDEC static - #define STBSP__PUBLICDEF static STBSP__ASAN - #else - #ifdef __cplusplus - #define STBSP__PUBLICDEC extern "C" - #define STBSP__PUBLICDEF extern "C" STBSP__ASAN - #else - #define STBSP__PUBLICDEC extern - #define STBSP__PUBLICDEF STBSP__ASAN - #endif - #endif - - #if defined(__has_attribute) - #if __has_attribute(format) - #define STBSP__ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) - #endif - #endif - - #ifndef STBSP__ATTRIBUTE_FORMAT - #define STBSP__ATTRIBUTE_FORMAT(fmt, va) - #endif - - #ifdef _MSC_VER - #define STBSP__NOTUSED(v) (void)(v) - #else - #define STBSP__NOTUSED(v) (void)sizeof(v) - #endif - - #include // for va_arg(), va_list() - #include // size_t, ptrdiff_t - - #ifndef STB_SPRINTF_MIN - #define STB_SPRINTF_MIN 512 // how many characters per callback - #endif -typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); - - #ifndef STB_SPRINTF_DECORATE - #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names - #endif - -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2, 3); -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3, 4); - -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); -STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); - -#endif // STB_SPRINTF_H_INCLUDE - -#ifdef STB_SPRINTF_IMPLEMENTATION - - #define stbsp__uint32 unsigned int - #define stbsp__int32 signed int - - #ifdef _MSC_VER - #define stbsp__uint64 unsigned __int64 - #define stbsp__int64 signed __int64 - #else - #define stbsp__uint64 unsigned long long - #define stbsp__int64 signed long long - #endif - #define stbsp__uint16 unsigned short - - #ifndef stbsp__uintptr - #if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) - #define stbsp__uintptr stbsp__uint64 - #else - #define stbsp__uintptr stbsp__uint32 - #endif - #endif - - #ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) - #if defined(_MSC_VER) && (_MSC_VER < 1900) - #define STB_SPRINTF_MSVC_MODE - #endif - #endif - - #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses - #define STBSP__UNALIGNED(code) - #else - #define STBSP__UNALIGNED(code) code - #endif - - #ifndef STB_SPRINTF_NOFLOAT -// internal float utility functions -static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); -static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); - #define STBSP__SPECIAL 0x7000 - #endif - -static char stbsp__period = '.'; -static char stbsp__comma = ','; -static struct -{ - short temp; // force next field to be 2-byte aligned - char pair[201]; -} stbsp__digitpair = - { - 0, - "00010203040506070809101112131415161718192021222324" - "25262728293031323334353637383940414243444546474849" - "50515253545556575859606162636465666768697071727374" - "75767778798081828384858687888990919293949596979899"}; - -STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) { - stbsp__period = pperiod; - stbsp__comma = pcomma; -} - - #define STBSP__LEFTJUST 1 - #define STBSP__LEADINGPLUS 2 - #define STBSP__LEADINGSPACE 4 - #define STBSP__LEADING_0X 8 - #define STBSP__LEADINGZERO 16 - #define STBSP__INTMAX 32 - #define STBSP__TRIPLET_COMMA 64 - #define STBSP__NEGATIVE 128 - #define STBSP__METRIC_SUFFIX 256 - #define STBSP__HALFWIDTH 512 - #define STBSP__METRIC_NOSPACE 1024 - #define STBSP__METRIC_1024 2048 - #define STBSP__METRIC_JEDEC 4096 - -static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) { - sign[0] = 0; - if (fl & STBSP__NEGATIVE) { - sign[0] = 1; - sign[1] = '-'; - } - else if (fl & STBSP__LEADINGSPACE) { - sign[0] = 1; - sign[1] = ' '; - } - else if (fl & STBSP__LEADINGPLUS) { - sign[0] = 1; - sign[1] = '+'; - } -} - -static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) { - char const *sn = s; - - // get up to 4-byte alignment - for (;;) { - if (((stbsp__uintptr)sn & 3) == 0) - break; - - if (!limit || *sn == 0) - return (stbsp__uint32)(sn - s); - - ++sn; - --limit; - } - - // scan over 4 bytes at a time to find terminating 0 - // this will intentionally scan up to 3 bytes past the end of buffers, - // but becase it works 4B aligned, it will never cross page boundaries - // (hence the STBSP__ASAN markup; the over-read here is intentional - // and harmless) - while (limit >= 4) { - stbsp__uint32 v = *(stbsp__uint32 *)sn; - // bit hack to find if there's a 0 byte in there - if ((v - 0x01010101) & (~v) & 0x80808080UL) - break; - - sn += 4; - limit -= 4; - } - - // handle the last few characters to find actual size - while (limit && *sn) { - ++sn; - --limit; - } - - return (stbsp__uint32)(sn - s); -} - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) { - static char hex[] = "0123456789abcdefxp"; - static char hexu[] = "0123456789ABCDEFXP"; - char *bf; - char const *f; - int tlen = 0; - - bf = buf; - f = fmt; - for (;;) { - stbsp__int32 fw, pr, tz; - stbsp__uint32 fl; - - // macros for the callback buffer stuff - #define stbsp__chk_cb_bufL(bytes) \ - { \ - int len = (int)(bf - buf); \ - if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ - tlen += len; \ - if (0 == (bf = buf = callback(buf, user, len))) \ - goto done; \ - } \ - } - #define stbsp__chk_cb_buf(bytes) \ - { \ - if (callback) { \ - stbsp__chk_cb_bufL(bytes); \ - } \ - } - #define stbsp__flush_cb() \ - { \ - stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ - } // flush if there is even one byte in the buffer - #define stbsp__cb_buf_clamp(cl, v) \ - cl = v; \ - if (callback) { \ - int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ - if (cl > lg) \ - cl = lg; \ - } - - // fast copy everything up to the next % (or end of string) - for (;;) { - while (((stbsp__uintptr)f) & 3) { - schk1: - if (f[0] == '%') - goto scandd; - schk2: - if (f[0] == 0) - goto endfmt; - stbsp__chk_cb_buf(1); - *bf++ = f[0]; - ++f; - } - for (;;) { - // Check if the next 4 bytes contain %(0x25) or end of string. - // Using the 'hasless' trick: - // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord - stbsp__uint32 v, c; - v = *(stbsp__uint32 *)f; - c = (~v) & 0x80808080; - if (((v ^ 0x25252525) - 0x01010101) & c) - goto schk1; - if ((v - 0x01010101) & c) - goto schk2; - if (callback) - if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) - goto schk1; - #ifdef STB_SPRINTF_NOUNALIGNED - if (((stbsp__uintptr)bf) & 3) { - bf[0] = f[0]; - bf[1] = f[1]; - bf[2] = f[2]; - bf[3] = f[3]; - } - else - #endif - { - *(stbsp__uint32 *)bf = v; - } - bf += 4; - f += 4; - } - } - scandd: - - ++f; - - // ok, we have a percent, read the modifiers first - fw = 0; - pr = -1; - fl = 0; - tz = 0; - - // flags - for (;;) { - switch (f[0]) { - // if we have left justify - case '-': - fl |= STBSP__LEFTJUST; - ++f; - continue; - // if we have leading plus - case '+': - fl |= STBSP__LEADINGPLUS; - ++f; - continue; - // if we have leading space - case ' ': - fl |= STBSP__LEADINGSPACE; - ++f; - continue; - // if we have leading 0x - case '#': - fl |= STBSP__LEADING_0X; - ++f; - continue; - // if we have thousand commas - case '\'': - fl |= STBSP__TRIPLET_COMMA; - ++f; - continue; - // if we have kilo marker (none->kilo->kibi->jedec) - case '$': - if (fl & STBSP__METRIC_SUFFIX) { - if (fl & STBSP__METRIC_1024) { - fl |= STBSP__METRIC_JEDEC; - } - else { - fl |= STBSP__METRIC_1024; - } - } - else { - fl |= STBSP__METRIC_SUFFIX; - } - ++f; - continue; - // if we don't want space between metric suffix and number - case '_': - fl |= STBSP__METRIC_NOSPACE; - ++f; - continue; - // if we have leading zero - case '0': - fl |= STBSP__LEADINGZERO; - ++f; - goto flags_done; - default: goto flags_done; - } - } - flags_done: - - // get the field width - if (f[0] == '*') { - fw = va_arg(va, stbsp__uint32); - ++f; - } - else { - while ((f[0] >= '0') && (f[0] <= '9')) { - fw = fw * 10 + f[0] - '0'; - f++; - } - } - // get the precision - if (f[0] == '.') { - ++f; - if (f[0] == '*') { - pr = va_arg(va, stbsp__uint32); - ++f; - } - else { - pr = 0; - while ((f[0] >= '0') && (f[0] <= '9')) { - pr = pr * 10 + f[0] - '0'; - f++; - } - } - } - - // handle integer size overrides - switch (f[0]) { - // are we halfwidth? - case 'h': - fl |= STBSP__HALFWIDTH; - ++f; - if (f[0] == 'h') - ++f; // QUARTERWIDTH - break; - // are we 64-bit (unix style) - case 'l': - fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); - ++f; - if (f[0] == 'l') { - fl |= STBSP__INTMAX; - ++f; - } - break; - // are we 64-bit on intmax? (c99) - case 'j': - fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; - ++f; - break; - // are we 64-bit on size_t or ptrdiff_t? (c99) - case 'z': - fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; - ++f; - break; - case 't': - fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; - ++f; - break; - // are we 64-bit (msft style) - case 'I': - if ((f[1] == '6') && (f[2] == '4')) { - fl |= STBSP__INTMAX; - f += 3; - } - else if ((f[1] == '3') && (f[2] == '2')) { - f += 3; - } - else { - fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); - ++f; - } - break; - default: break; - } - - // handle each replacement - switch (f[0]) { - #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 - char num[STBSP__NUMSZ]; - char lead[8]; - char tail[8]; - char *s; - String str; - char const *h; - stbsp__uint32 l, n, cs; - stbsp__uint64 n64; - #ifndef STB_SPRINTF_NOFLOAT - double fv; - #endif - stbsp__int32 dp; - char const *sn; - - case 'Q': - str = va_arg(va, String); - if (str.str == 0 && str.len != 0) - str = string_null; - pr = (int)str.len; - s = (char *)str.str; - l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - goto scopy; - - case 's': - // get the string - s = va_arg(va, char *); - if (s == 0) - s = (char *)"null"; - // get the length, limited to desired precision - // always limit to ~0u chars since our counts are 32b - l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - // copy the string in - goto scopy; - - case 'c': // char - // get the character - s = num + STBSP__NUMSZ - 1; - *s = (char)va_arg(va, int); - l = 1; - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - goto scopy; - - case 'n': // weird write-bytes specifier - { - int *d = va_arg(va, int *); - *d = tlen + (int)(bf - buf); - } break; - - #ifdef STB_SPRINTF_NOFLOAT - case 'A': // float - case 'a': // hex float - case 'G': // float - case 'g': // float - case 'E': // float - case 'e': // float - case 'f': // float - va_arg(va, double); // eat it - s = (char *)"No float"; - l = 8; - lead[0] = 0; - tail[0] = 0; - pr = 0; - cs = 0; - STBSP__NOTUSED(dp); - goto scopy; - #else - case 'A': // hex float - case 'a': // hex float - h = (f[0] == 'A') ? hexu : hex; - fv = va_arg(va, double); - if (pr == -1) - pr = 6; // default is 6 - // read the double into a string - if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) - fl |= STBSP__NEGATIVE; - - s = num + 64; - - stbsp__lead_sign(fl, lead); - - if (dp == -1023) - dp = (n64) ? -1022 : 0; - else - n64 |= (((stbsp__uint64)1) << 52); - n64 <<= (64 - 56); - if (pr < 15) - n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); - // add leading chars - - #ifdef STB_SPRINTF_MSVC_MODE - *s++ = '0'; - *s++ = 'x'; - #else - lead[1 + lead[0]] = '0'; - lead[2 + lead[0]] = 'x'; - lead[0] += 2; - #endif - *s++ = h[(n64 >> 60) & 15]; - n64 <<= 4; - if (pr) - *s++ = stbsp__period; - sn = s; - - // print the bits - n = pr; - if (n > 13) - n = 13; - if (pr > (stbsp__int32)n) - tz = pr - n; - pr = 0; - while (n--) { - *s++ = h[(n64 >> 60) & 15]; - n64 <<= 4; - } - - // print the expo - tail[1] = h[17]; - if (dp < 0) { - tail[2] = '-'; - dp = -dp; - } - else - tail[2] = '+'; - n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); - tail[0] = (char)n; - for (;;) { - tail[n] = '0' + dp % 10; - if (n <= 3) - break; - --n; - dp /= 10; - } - - dp = (int)(s - sn); - l = (int)(s - (num + 64)); - s = num + 64; - cs = 1 + (3 << 24); - goto scopy; - - case 'G': // float - case 'g': // float - h = (f[0] == 'G') ? hexu : hex; - fv = va_arg(va, double); - if (pr == -1) - pr = 6; - else if (pr == 0) - pr = 1; // default is 6 - // read the double into a string - if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) - fl |= STBSP__NEGATIVE; - - // clamp the precision and delete extra zeros after clamp - n = pr; - if (l > (stbsp__uint32)pr) - l = pr; - while ((l > 1) && (pr) && (sn[l - 1] == '0')) { - --pr; - --l; - } - - // should we use %e - if ((dp <= -4) || (dp > (stbsp__int32)n)) { - if (pr > (stbsp__int32)l) - pr = l - 1; - else if (pr) - --pr; // when using %e, there is one digit before the decimal - goto doexpfromg; - } - // this is the insane action to get the pr to match %g semantics for %f - if (dp > 0) { - pr = (dp < (stbsp__int32)l) ? l - dp : 0; - } - else { - pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32)l : pr); - } - goto dofloatfromg; - - case 'E': // float - case 'e': // float - h = (f[0] == 'E') ? hexu : hex; - fv = va_arg(va, double); - if (pr == -1) - pr = 6; // default is 6 - // read the double into a string - if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) - fl |= STBSP__NEGATIVE; - doexpfromg: - tail[0] = 0; - stbsp__lead_sign(fl, lead); - if (dp == STBSP__SPECIAL) { - s = (char *)sn; - cs = 0; - pr = 0; - goto scopy; - } - s = num + 64; - // handle leading chars - *s++ = sn[0]; - - if (pr) - *s++ = stbsp__period; - - // handle after decimal - if ((l - 1) > (stbsp__uint32)pr) - l = pr + 1; - for (n = 1; n < l; n++) - *s++ = sn[n]; - // trailing zeros - tz = pr - (l - 1); - pr = 0; - // dump expo - tail[1] = h[0xe]; - dp -= 1; - if (dp < 0) { - tail[2] = '-'; - dp = -dp; - } - else - tail[2] = '+'; - #ifdef STB_SPRINTF_MSVC_MODE - n = 5; - #else - n = (dp >= 100) ? 5 : 4; - #endif - tail[0] = (char)n; - for (;;) { - tail[n] = '0' + dp % 10; - if (n <= 3) - break; - --n; - dp /= 10; - } - cs = 1 + (3 << 24); // how many tens - goto flt_lead; - - case 'f': // float - fv = va_arg(va, double); - doafloat: - // do kilos - if (fl & STBSP__METRIC_SUFFIX) { - double divisor; - divisor = 1000.0f; - if (fl & STBSP__METRIC_1024) - divisor = 1024.0; - while (fl < 0x4000000) { - if ((fv < divisor) && (fv > -divisor)) - break; - fv /= divisor; - fl += 0x1000000; - } - } - if (pr == -1) - pr = 6; // default is 6 - // read the double into a string - if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) - fl |= STBSP__NEGATIVE; - dofloatfromg: - tail[0] = 0; - stbsp__lead_sign(fl, lead); - if (dp == STBSP__SPECIAL) { - s = (char *)sn; - cs = 0; - pr = 0; - goto scopy; - } - s = num + 64; - - // handle the three decimal varieties - if (dp <= 0) { - stbsp__int32 i; - // handle 0.000*000xxxx - *s++ = '0'; - if (pr) - *s++ = stbsp__period; - n = -dp; - if ((stbsp__int32)n > pr) - n = pr; - i = n; - while (i) { - if ((((stbsp__uintptr)s) & 3) == 0) - break; - *s++ = '0'; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)s = 0x30303030; - s += 4; - i -= 4; - } - while (i) { - *s++ = '0'; - --i; - } - if ((stbsp__int32)(l + n) > pr) - l = pr - n; - i = l; - while (i) { - *s++ = *sn++; - --i; - } - tz = pr - (n + l); - cs = 1 + (3 << 24); // how many tens did we write (for commas below) - } - else { - cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; - if ((stbsp__uint32)dp >= l) { - // handle xxxx000*000.0 - n = 0; - for (;;) { - if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { - cs = 0; - *s++ = stbsp__comma; - } - else { - *s++ = sn[n]; - ++n; - if (n >= l) - break; - } - } - if (n < (stbsp__uint32)dp) { - n = dp - n; - if ((fl & STBSP__TRIPLET_COMMA) == 0) { - while (n) { - if ((((stbsp__uintptr)s) & 3) == 0) - break; - *s++ = '0'; - --n; - } - while (n >= 4) { - *(stbsp__uint32 *)s = 0x30303030; - s += 4; - n -= 4; - } - } - while (n) { - if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { - cs = 0; - *s++ = stbsp__comma; - } - else { - *s++ = '0'; - --n; - } - } - } - cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens - if (pr) { - *s++ = stbsp__period; - tz = pr; - } - } - else { - // handle xxxxx.xxxx000*000 - n = 0; - for (;;) { - if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { - cs = 0; - *s++ = stbsp__comma; - } - else { - *s++ = sn[n]; - ++n; - if (n >= (stbsp__uint32)dp) - break; - } - } - cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens - if (pr) - *s++ = stbsp__period; - if ((l - dp) > (stbsp__uint32)pr) - l = pr + dp; - while (n < l) { - *s++ = sn[n]; - ++n; - } - tz = pr - (l - dp); - } - } - pr = 0; - - // handle k,m,g,t - if (fl & STBSP__METRIC_SUFFIX) { - char idx; - idx = 1; - if (fl & STBSP__METRIC_NOSPACE) - idx = 0; - tail[0] = idx; - tail[1] = ' '; - { - if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. - if (fl & STBSP__METRIC_1024) - tail[idx + 1] = "_KMGT"[fl >> 24]; - else - tail[idx + 1] = "_kMGT"[fl >> 24]; - idx++; - // If printing kibits and not in jedec, add the 'i'. - if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { - tail[idx + 1] = 'i'; - idx++; - } - tail[0] = idx; - } - } - }; - - flt_lead: - // get the length that we copied - l = (stbsp__uint32)(s - (num + 64)); - s = num + 64; - goto scopy; - #endif - - case 'B': // upper binary - case 'b': // lower binary - h = (f[0] == 'B') ? hexu : hex; - lead[0] = 0; - if (fl & STBSP__LEADING_0X) { - lead[0] = 2; - lead[1] = '0'; - lead[2] = h[0xb]; - } - l = (8 << 4) | (1 << 8); - goto radixnum; - - case 'o': // octal - h = hexu; - lead[0] = 0; - if (fl & STBSP__LEADING_0X) { - lead[0] = 1; - lead[1] = '0'; - } - l = (3 << 4) | (3 << 8); - goto radixnum; - - case 'p': // pointer - fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; - pr = sizeof(void *) * 2; - fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros - // fall through - to X - - case 'X': // upper hex - case 'x': // lower hex - h = (f[0] == 'X') ? hexu : hex; - l = (4 << 4) | (4 << 8); - lead[0] = 0; - if (fl & STBSP__LEADING_0X) { - lead[0] = 2; - lead[1] = '0'; - lead[2] = h[16]; - } - radixnum: - // get the number - if (fl & STBSP__INTMAX) - n64 = va_arg(va, stbsp__uint64); - else - n64 = va_arg(va, stbsp__uint32); - - s = num + STBSP__NUMSZ; - dp = 0; - // clear tail, and clear leading if value is zero - tail[0] = 0; - if (n64 == 0) { - lead[0] = 0; - if (pr == 0) { - l = 0; - cs = 0; - goto scopy; - } - } - // convert to string - for (;;) { - *--s = h[n64 & ((1 << (l >> 8)) - 1)]; - n64 >>= (l >> 8); - if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) - break; - if (fl & STBSP__TRIPLET_COMMA) { - ++l; - if ((l & 15) == ((l >> 4) & 15)) { - l &= ~15; - *--s = stbsp__comma; - } - } - }; - // get the tens and the comma pos - cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); - // get the length that we copied - l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); - // copy it - goto scopy; - - case 'u': // unsigned - case 'i': - case 'd': // integer - // get the integer and abs it - if (fl & STBSP__INTMAX) { - stbsp__int64 i64 = va_arg(va, stbsp__int64); - n64 = (stbsp__uint64)i64; - if ((f[0] != 'u') && (i64 < 0)) { - n64 = (stbsp__uint64)-i64; - fl |= STBSP__NEGATIVE; - } - } - else { - stbsp__int32 i = va_arg(va, stbsp__int32); - n64 = (stbsp__uint32)i; - if ((f[0] != 'u') && (i < 0)) { - n64 = (stbsp__uint32)-i; - fl |= STBSP__NEGATIVE; - } - } - - #ifndef STB_SPRINTF_NOFLOAT - if (fl & STBSP__METRIC_SUFFIX) { - if (n64 < 1024) - pr = 0; - else if (pr == -1) - pr = 1; - fv = (double)(stbsp__int64)n64; - goto doafloat; - } - #endif - - // convert to string - s = num + STBSP__NUMSZ; - l = 0; - - for (;;) { - // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) - char *o = s - 8; - if (n64 >= 100000000) { - n = (stbsp__uint32)(n64 % 100000000); - n64 /= 100000000; - } - else { - n = (stbsp__uint32)n64; - n64 = 0; - } - if ((fl & STBSP__TRIPLET_COMMA) == 0) { - do { - s -= 2; - *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; - n /= 100; - } while (n); - } - while (n) { - if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { - l = 0; - *--s = stbsp__comma; - --o; - } - else { - *--s = (char)(n % 10) + '0'; - n /= 10; - } - } - if (n64 == 0) { - if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) - ++s; - break; - } - while (s != o) - if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { - l = 0; - *--s = stbsp__comma; - --o; - } - else { - *--s = '0'; - } - } - - tail[0] = 0; - stbsp__lead_sign(fl, lead); - - // get the length that we copied - l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); - if (l == 0) { - *--s = '0'; - l = 1; - } - cs = l + (3 << 24); - if (pr < 0) - pr = 0; - - scopy: - // get fw=leading/trailing space, pr=leading zeros - if (pr < (stbsp__int32)l) - pr = l; - n = pr + lead[0] + tail[0] + tz; - if (fw < (stbsp__int32)n) - fw = n; - fw -= n; - pr -= l; - - // handle right justify and leading zeros - if ((fl & STBSP__LEFTJUST) == 0) { - if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr - { - pr = (fw > pr) ? fw : pr; - fw = 0; - } - else { - fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas - } - } - - // copy the spaces and/or zeros - if (fw + pr) { - stbsp__int32 i; - stbsp__uint32 c; - - // copy leading spaces (or when doing %8.4d stuff) - if ((fl & STBSP__LEFTJUST) == 0) - while (fw > 0) { - stbsp__cb_buf_clamp(i, fw); - fw -= i; - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = ' '; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x20202020; - bf += 4; - i -= 4; - } - while (i) { - *bf++ = ' '; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy leader - sn = lead + 1; - while (lead[0]) { - stbsp__cb_buf_clamp(i, lead[0]); - lead[0] -= (char)i; - while (i) { - *bf++ = *sn++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy leading zeros - c = cs >> 24; - cs &= 0xffffff; - cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; - while (pr > 0) { - stbsp__cb_buf_clamp(i, pr); - pr -= i; - if ((fl & STBSP__TRIPLET_COMMA) == 0) { - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = '0'; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x30303030; - bf += 4; - i -= 4; - } - } - while (i) { - if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { - cs = 0; - *bf++ = stbsp__comma; - } - else - *bf++ = '0'; - --i; - } - stbsp__chk_cb_buf(1); - } - } - - // copy leader if there is still one - sn = lead + 1; - while (lead[0]) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, lead[0]); - lead[0] -= (char)i; - while (i) { - *bf++ = *sn++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy the string - n = l; - while (n) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, n); - n -= i; - STBSP__UNALIGNED(while (i >= 4) { - *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; - bf += 4; - s += 4; - i -= 4; - }) - while (i) { - *bf++ = *s++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy trailing zeros - while (tz) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, tz); - tz -= i; - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = '0'; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x30303030; - bf += 4; - i -= 4; - } - while (i) { - *bf++ = '0'; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy tail if there is one - sn = tail + 1; - while (tail[0]) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, tail[0]); - tail[0] -= (char)i; - while (i) { - *bf++ = *sn++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // handle the left justify - if (fl & STBSP__LEFTJUST) - if (fw > 0) { - while (fw) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, fw); - fw -= i; - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = ' '; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x20202020; - bf += 4; - i -= 4; - } - while (i--) - *bf++ = ' '; - stbsp__chk_cb_buf(1); - } - } - break; - - default: // unknown, just copy code - s = num + STBSP__NUMSZ - 1; - *s = f[0]; - l = 1; - fw = fl = 0; - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - goto scopy; - } - ++f; - } -endfmt: - - if (!callback) - *bf = 0; - else - stbsp__flush_cb(); - -done: - return tlen + (int)(bf - buf); -} - - // cleanup - #undef STBSP__LEFTJUST - #undef STBSP__LEADINGPLUS - #undef STBSP__LEADINGSPACE - #undef STBSP__LEADING_0X - #undef STBSP__LEADINGZERO - #undef STBSP__INTMAX - #undef STBSP__TRIPLET_COMMA - #undef STBSP__NEGATIVE - #undef STBSP__METRIC_SUFFIX - #undef STBSP__NUMSZ - #undef stbsp__chk_cb_bufL - #undef stbsp__chk_cb_buf - #undef stbsp__flush_cb - #undef stbsp__cb_buf_clamp - -// ============================================================================ -// wrapper functions - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) { - int result; - va_list va; - va_start(va, fmt); - result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); - va_end(va); - return result; -} - -typedef struct stbsp__context { - char *buf; - int count; - int length; - char tmp[STB_SPRINTF_MIN]; -} stbsp__context; - -static char *stbsp__clamp_callback(const char *buf, void *user, int len) { - stbsp__context *c = (stbsp__context *)user; - c->length += len; - - if (len > c->count) - len = c->count; - - if (len) { - if (buf != c->buf) { - const char *s, *se; - char *d; - d = c->buf; - s = buf; - se = buf + len; - do { - *d++ = *s++; - } while (s < se); - } - c->buf += len; - c->count -= len; - } - - if (c->count <= 0) - return c->tmp; - return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can -} - -static char *stbsp__count_clamp_callback(const char *buf, void *user, int len) { - stbsp__context *c = (stbsp__context *)user; - (void)sizeof(buf); - - c->length += len; - return c->tmp; // go direct into buffer if you can -} - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va) { - stbsp__context c; - - if ((count == 0) && !buf) { - c.length = 0; - - STB_SPRINTF_DECORATE(vsprintfcb) - (stbsp__count_clamp_callback, &c, c.tmp, fmt, va); - } - else { - int l; - - c.buf = buf; - c.count = count; - c.length = 0; - - STB_SPRINTF_DECORATE(vsprintfcb) - (stbsp__clamp_callback, &c, stbsp__clamp_callback(0, &c, 0), fmt, va); - - // zero-terminate - l = (int)(c.buf - buf); - if (l >= count) // should never be greater, only equal (or less) than count - l = count - 1; - buf[l] = 0; - } - - return c.length; -} - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) { - int result; - va_list va; - va_start(va, fmt); - - result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); - va_end(va); - - return result; -} - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) { - return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); -} - -// ======================================================================= -// low level float utility functions - - #ifndef STB_SPRINTF_NOFLOAT - - // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) - #define STBSP__COPYFP(dest, src) \ - { \ - int cn; \ - for (cn = 0; cn < 8; cn++) \ - ((char *)&dest)[cn] = ((char *)&src)[cn]; \ - } - -// get float info -static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) { - double d; - stbsp__int64 b = 0; - - // load value and round at the frac_digits - d = value; - - STBSP__COPYFP(b, d); - - *bits = b & ((((stbsp__uint64)1) << 52) - 1); - *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); - - return (stbsp__int32)((stbsp__uint64)b >> 63); -} - -static double const stbsp__bot[23] = { - 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, - 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022}; -static double const stbsp__negbot[22] = { - 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, - 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022}; -static double const stbsp__negboterr[22] = { - -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, - 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, - -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, - 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039}; -static double const stbsp__top[13] = { - 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299}; -static double const stbsp__negtop[13] = { - 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299}; -static double const stbsp__toperr[13] = { - 8388608, - 6.8601809640529717e+028, - -7.253143638152921e+052, - -4.3377296974619174e+075, - -1.5559416129466825e+098, - -3.2841562489204913e+121, - -3.7745893248228135e+144, - -1.7356668416969134e+167, - -3.8893577551088374e+190, - -9.9566444326005119e+213, - 6.3641293062232429e+236, - -5.2069140800249813e+259, - -5.2504760255204387e+282}; -static double const stbsp__negtoperr[13] = { - 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, - -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, - 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, - 8.0970921678014997e-317}; - - #if defined(_MSC_VER) && (_MSC_VER <= 1200) -static stbsp__uint64 const stbsp__powten[20] = { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, - 10000000000000000000U}; - #define stbsp__tento19th ((stbsp__uint64)1000000000000000000) - #else -static stbsp__uint64 const stbsp__powten[20] = { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000ULL, - 100000000000ULL, - 1000000000000ULL, - 10000000000000ULL, - 100000000000000ULL, - 1000000000000000ULL, - 10000000000000000ULL, - 100000000000000000ULL, - 1000000000000000000ULL, - 10000000000000000000ULL}; - #define stbsp__tento19th (1000000000000000000ULL) - #endif - - #define stbsp__ddmulthi(oh, ol, xh, yh) \ - { \ - double ahi = 0, alo, bhi = 0, blo; \ - stbsp__int64 bt; \ - oh = xh * yh; \ - STBSP__COPYFP(bt, xh); \ - bt &= ((~(stbsp__uint64)0) << 27); \ - STBSP__COPYFP(ahi, bt); \ - alo = xh - ahi; \ - STBSP__COPYFP(bt, yh); \ - bt &= ((~(stbsp__uint64)0) << 27); \ - STBSP__COPYFP(bhi, bt); \ - blo = yh - bhi; \ - ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ - } - - #define stbsp__ddtoS64(ob, xh, xl) \ - { \ - double ahi = 0, alo, vh, t; \ - ob = (stbsp__int64)xh; \ - vh = (double)ob; \ - ahi = (xh - vh); \ - t = (ahi - xh); \ - alo = (xh - (ahi - t)) - (vh + t); \ - ob += (stbsp__int64)(ahi + alo + xl); \ - } - - #define stbsp__ddrenorm(oh, ol) \ - { \ - double s; \ - s = oh + ol; \ - ol = ol - (s - oh); \ - oh = s; \ - } - - #define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); - - #define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); - -static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 -{ - double ph, pl; - if ((power >= 0) && (power <= 22)) { - stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); - } - else { - stbsp__int32 e, et, eb; - double p2h, p2l; - - e = power; - if (power < 0) - e = -e; - et = (e * 0x2c9) >> 14; /* %23 */ - if (et > 13) - et = 13; - eb = e - (et * 23); - - ph = d; - pl = 0.0; - if (power < 0) { - if (eb) { - --eb; - stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); - stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); - } - if (et) { - stbsp__ddrenorm(ph, pl); - --et; - stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); - stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); - ph = p2h; - pl = p2l; - } - } - else { - if (eb) { - e = eb; - if (eb > 22) - eb = 22; - e -= eb; - stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); - if (e) { - stbsp__ddrenorm(ph, pl); - stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); - stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); - ph = p2h; - pl = p2l; - } - } - if (et) { - stbsp__ddrenorm(ph, pl); - --et; - stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); - stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); - ph = p2h; - pl = p2l; - } - } - } - stbsp__ddrenorm(ph, pl); - *ohi = ph; - *olo = pl; -} - -// given a float value, returns the significant bits in bits, and the position of the -// decimal point in decimal_pos. +/-INF and NAN are specified by special values -// returned in the decimal_pos parameter. -// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 -static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) { - double d; - stbsp__int64 bits = 0; - stbsp__int32 expo, e, ng, tens; - - d = value; - STBSP__COPYFP(bits, d); - expo = (stbsp__int32)((bits >> 52) & 2047); - ng = (stbsp__int32)((stbsp__uint64)bits >> 63); - if (ng) - d = -d; - - if (expo == 2047) // is nan or inf? - { - *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; - *decimal_pos = STBSP__SPECIAL; - *len = 3; - return ng; - } - - if (expo == 0) // is zero or denormal - { - if (((stbsp__uint64)bits << 1) == 0) // do zero - { - *decimal_pos = 1; - *start = out; - out[0] = '0'; - *len = 1; - return ng; - } - // find the right expo for denormals - { - stbsp__int64 v = ((stbsp__uint64)1) << 51; - while ((bits & v) == 0) { - --expo; - v >>= 1; - } - } - } - - // find the decimal exponent as well as the decimal bits of the value - { - double ph, pl; - - // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 - tens = expo - 1023; - tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); - - // move the significant bits into position and stick them into an int - stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); - - // get full as much precision from double-double as possible - stbsp__ddtoS64(bits, ph, pl); - - // check if we undershot - if (((stbsp__uint64)bits) >= stbsp__tento19th) - ++tens; - } - - // now do the rounding in integer land - frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); - if ((frac_digits < 24)) { - stbsp__uint32 dg = 1; - if ((stbsp__uint64)bits >= stbsp__powten[9]) - dg = 10; - while ((stbsp__uint64)bits >= stbsp__powten[dg]) { - ++dg; - if (dg == 20) - goto noround; - } - if (frac_digits < dg) { - stbsp__uint64 r; - // add 0.5 at the right position and round - e = dg - frac_digits; - if ((stbsp__uint32)e >= 24) - goto noround; - r = stbsp__powten[e]; - bits = bits + (r / 2); - if ((stbsp__uint64)bits >= stbsp__powten[dg]) - ++tens; - bits /= r; - } - noround:; - } - - // kill long trailing runs of zeros - if (bits) { - stbsp__uint32 n; - for (;;) { - if (bits <= 0xffffffff) - break; - if (bits % 1000) - goto donez; - bits /= 1000; - } - n = (stbsp__uint32)bits; - while ((n % 1000) == 0) - n /= 1000; - bits = n; - donez:; - } - - // convert to string - out += 64; - e = 0; - for (;;) { - stbsp__uint32 n; - char *o = out - 8; - // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) - if (bits >= 100000000) { - n = (stbsp__uint32)(bits % 100000000); - bits /= 100000000; - } - else { - n = (stbsp__uint32)bits; - bits = 0; - } - while (n) { - out -= 2; - *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; - n /= 100; - e += 2; - } - if (bits == 0) { - if ((e) && (out[0] == '0')) { - ++out; - --e; - } - break; - } - while (out != o) { - *--out = '0'; - ++e; - } - } - - *decimal_pos = tens; - *start = out; - *len = e; - return ng; -} - - #undef stbsp__ddmulthi - #undef stbsp__ddrenorm - #undef stbsp__ddmultlo - #undef stbsp__ddmultlos - #undef STBSP__SPECIAL - #undef STBSP__COPYFP - - #endif // STB_SPRINTF_NOFLOAT - - // clean up - #undef stbsp__uint16 - #undef stbsp__uint32 - #undef stbsp__int32 - #undef stbsp__uint64 - #undef stbsp__int64 - #undef STBSP__UNALIGNED - -#endif // STB_SPRINTF_IMPLEMENTATION - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE + #define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) + typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); + Convert into a buffer, calling back every STB_SPRINTF_MIN chars. + Your callback can then copy the chars out, print them or whatever. + This function is actually the workhorse for everything else. + The buffer you pass in must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + + #if defined(__clang__) + #if defined(__has_feature) && defined(__has_attribute) + #if __has_feature(address_sanitizer) + #if __has_attribute(__no_sanitize__) + #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) + #elif __has_attribute(__no_sanitize_address__) + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #elif __has_attribute(__no_address_safety_analysis__) + #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) + #endif + #endif + #endif + #elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #endif + #endif + + #ifndef STBSP__ASAN + #define STBSP__ASAN + #endif + + #ifdef STB_SPRINTF_STATIC + #define STBSP__PUBLICDEC static + #define STBSP__PUBLICDEF static STBSP__ASAN + #else + #ifdef __cplusplus + #define STBSP__PUBLICDEC extern "C" + #define STBSP__PUBLICDEF extern "C" STBSP__ASAN + #else + #define STBSP__PUBLICDEC extern + #define STBSP__PUBLICDEF STBSP__ASAN + #endif + #endif + + #if defined(__has_attribute) + #if __has_attribute(format) + #define STBSP__ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) + #endif + #endif + + #ifndef STBSP__ATTRIBUTE_FORMAT + #define STBSP__ATTRIBUTE_FORMAT(fmt, va) + #endif + + #ifdef _MSC_VER + #define STBSP__NOTUSED(v) (void)(v) + #else + #define STBSP__NOTUSED(v) (void)sizeof(v) + #endif + + #include // for va_arg(), va_list() + #include // size_t, ptrdiff_t + + #ifndef STB_SPRINTF_MIN + #define STB_SPRINTF_MIN 512 // how many characters per callback + #endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + + #ifndef STB_SPRINTF_DECORATE + #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names + #endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2, 3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3, 4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + + #define stbsp__uint32 unsigned int + #define stbsp__int32 signed int + + #ifdef _MSC_VER + #define stbsp__uint64 unsigned __int64 + #define stbsp__int64 signed __int64 + #else + #define stbsp__uint64 unsigned long long + #define stbsp__int64 signed long long + #endif + #define stbsp__uint16 unsigned short + + #ifndef stbsp__uintptr + #if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) + #define stbsp__uintptr stbsp__uint64 + #else + #define stbsp__uintptr stbsp__uint32 + #endif + #endif + + #ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) + #if defined(_MSC_VER) && (_MSC_VER < 1900) + #define STB_SPRINTF_MSVC_MODE + #endif + #endif + + #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses + #define STBSP__UNALIGNED(code) + #else + #define STBSP__UNALIGNED(code) code + #endif + + #ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); + #define STBSP__SPECIAL 0x7000 + #endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = + { + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899"}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) { + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + + #define STBSP__LEFTJUST 1 + #define STBSP__LEADINGPLUS 2 + #define STBSP__LEADINGSPACE 4 + #define STBSP__LEADING_0X 8 + #define STBSP__LEADINGZERO 16 + #define STBSP__INTMAX 32 + #define STBSP__TRIPLET_COMMA 64 + #define STBSP__NEGATIVE 128 + #define STBSP__METRIC_SUFFIX 256 + #define STBSP__HALFWIDTH 512 + #define STBSP__METRIC_NOSPACE 1024 + #define STBSP__METRIC_1024 2048 + #define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) { + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } + else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } + else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) { + char const *sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) { + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff + #define stbsp__chk_cb_bufL(bytes) \ + { \ + int len = (int)(bf - buf); \ + if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ + tlen += len; \ + if (0 == (bf = buf = callback(buf, user, len))) \ + goto done; \ + } \ + } + #define stbsp__chk_cb_buf(bytes) \ + { \ + if (callback) { \ + stbsp__chk_cb_bufL(bytes); \ + } \ + } + #define stbsp__flush_cb() \ + { \ + stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ + } // flush if there is even one byte in the buffer + #define stbsp__cb_buf_clamp(cl, v) \ + cl = v; \ + if (callback) { \ + int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ + if (cl > lg) \ + cl = lg; \ + } + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; + #ifdef STB_SPRINTF_NOUNALIGNED + if (((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } + else + #endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } + else { + fl |= STBSP__METRIC_1024; + } + } + else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } + else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } + else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } + else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } + else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { + #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + String str; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; + #ifndef STB_SPRINTF_NOFLOAT + double fv; + #endif + stbsp__int32 dp; + char const *sn; + + case 'Q': + str = va_arg(va, String); + if (str.str == 0 && str.len != 0) + str = string_null; + pr = (int)str.len; + s = (char *)str.str; + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + + #ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; + #else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); + // add leading chars + + #ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; + #else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; + #endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } + else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } + else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32)l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } + else + tail[2] = '+'; + #ifdef STB_SPRINTF_MSVC_MODE + n = 5; + #else + n = (dp >= 100) ? 5 : 4; + #endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } + else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } + else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } + else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } + else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } + else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; + #endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } + else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + + #ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } + #endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } + else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } + else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } + else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } + else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } + else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } +endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + +done: + return tlen + (int)(bf - buf); +} + + // cleanup + #undef STBSP__LEFTJUST + #undef STBSP__LEADINGPLUS + #undef STBSP__LEADINGSPACE + #undef STBSP__LEADING_0X + #undef STBSP__LEADINGZERO + #undef STBSP__INTMAX + #undef STBSP__TRIPLET_COMMA + #undef STBSP__NEGATIVE + #undef STBSP__METRIC_SUFFIX + #undef STBSP__NUMSZ + #undef stbsp__chk_cb_bufL + #undef stbsp__chk_cb_buf + #undef stbsp__flush_cb + #undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) { + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) { + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +static char *stbsp__count_clamp_callback(const char *buf, void *user, int len) { + stbsp__context *c = (stbsp__context *)user; + (void)sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va) { + stbsp__context c; + + if ((count == 0) && !buf) { + c.length = 0; + + STB_SPRINTF_DECORATE(vsprintfcb) + (stbsp__count_clamp_callback, &c, c.tmp, fmt, va); + } + else { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE(vsprintfcb) + (stbsp__clamp_callback, &c, stbsp__clamp_callback(0, &c, 0), fmt, va); + + // zero-terminate + l = (int)(c.buf - buf); + if (l >= count) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) { + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) { + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + + #ifndef STB_SPRINTF_NOFLOAT + + // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) + #define STBSP__COPYFP(dest, src) \ + { \ + int cn; \ + for (cn = 0; cn < 8; cn++) \ + ((char *)&dest)[cn] = ((char *)&src)[cn]; \ + } + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) { + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64)b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299}; +static double const stbsp__toperr[13] = { + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317}; + + #if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U}; + #define stbsp__tento19th ((stbsp__uint64)1000000000000000000) + #else +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL}; + #define stbsp__tento19th (1000000000000000000ULL) + #endif + + #define stbsp__ddmulthi(oh, ol, xh, yh) \ + { \ + double ahi = 0, alo, bhi = 0, blo; \ + stbsp__int64 bt; \ + oh = xh * yh; \ + STBSP__COPYFP(bt, xh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(ahi, bt); \ + alo = xh - ahi; \ + STBSP__COPYFP(bt, yh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(bhi, bt); \ + blo = yh - bhi; \ + ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ + } + + #define stbsp__ddtoS64(ob, xh, xl) \ + { \ + double ahi = 0, alo, vh, t; \ + ob = (stbsp__int64)xh; \ + vh = (double)ob; \ + ahi = (xh - vh); \ + t = (ahi - xh); \ + alo = (xh - (ahi - t)) - (vh + t); \ + ob += (stbsp__int64)(ahi + alo + xl); \ + } + + #define stbsp__ddrenorm(oh, ol) \ + { \ + double s; \ + s = oh + ol; \ + ol = ol - (s - oh); \ + oh = s; \ + } + + #define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + + #define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } + else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } + else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) { + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64)bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64)bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } + else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + + #undef stbsp__ddmulthi + #undef stbsp__ddrenorm + #undef stbsp__ddmultlo + #undef stbsp__ddmultlos + #undef STBSP__SPECIAL + #undef STBSP__COPYFP + + #endif // STB_SPRINTF_NOFLOAT + + // clean up + #undef stbsp__uint16 + #undef stbsp__uint32 + #undef stbsp__int32 + #undef stbsp__uint64 + #undef stbsp__int64 + #undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/c3_big_int.cpp b/src/language/c3_big_int.cpp similarity index 96% rename from c3_big_int.cpp rename to src/language/c3_big_int.cpp index 9ffe10e..b79ac74 100644 --- a/c3_big_int.cpp +++ b/src/language/c3_big_int.cpp @@ -1,1905 +1,1905 @@ -// Copyright (c) 2019 Christoffer Lerno. All rights reserved. -// Use of this source code is governed by the GNU LGPLv3.0 license -// a copy of which can be found in the LICENSE file. - -#define Set_BigInt_Arena(x) BigInt_Arena bigint_allocator(x) -struct BigInt_Arena { - Allocator *old; - BigInt_Arena(Allocator *allocator) { - old = bigint_allocator; - bigint_allocator = allocator; - } - ~BigInt_Arena() { bigint_allocator = old; } -}; - -CORE_Static BigInt -bigint_u64(U64 value) { - BigInt result; - bigint_init_unsigned(&result, value); - return result; -} - -CORE_Static BigInt -bigint_s64(S64 value) { - BigInt result; - bigint_init_signed(&result, value); - return result; -} - -CORE_Static BigInt -bigint_mul(const BigInt *a, const BigInt *b) { - BigInt result; - bigint_mul(&result, a, b); - return result; -} - -CORE_Static BigInt -bigint_add(const BigInt *a, const BigInt *b) { - BigInt result; - bigint_add(&result, a, b); - return result; -} - -CORE_Static BigInt -bigint_copy(Allocator *allocator, BigInt *src) { - BigInt dest = {}; - if (src->digit_count == 0) { - bigint_init_unsigned(&dest, 0); - return dest; - } - if (src->digit_count == 1) { - dest.digit_count = 1; - dest.digit = src->digit; - dest.is_negative = src->is_negative; - return dest; - } - dest.is_negative = src->is_negative; - dest.digit_count = src->digit_count; - - dest.digits = allocate_array(allocator, uint64_t, dest.digit_count); - memory_copy(dest.digits, src->digits, sizeof(uint64_t) * dest.digit_count); - return dest; -} - -//----------------------------------------------------------------------------- -// -//----------------------------------------------------------------------------- -static inline uint32_t u32_min(uint32_t a, uint32_t b) { - return a < b ? a : b; -} - -static inline size_t size_max(size_t a, size_t b) { - return a > b ? a : b; -} - -static inline unsigned unsigned_max(unsigned a, unsigned b) { - return a > b ? a : b; -} - -static inline const uint64_t *bigint_ptr(const BigInt *big_int) { - return big_int->digit_count == 1 ? &big_int->digit : big_int->digits; -} - -static void normalize(BigInt *big_int) { - const uint64_t *digits = bigint_ptr(big_int); - unsigned last_non_zero = UINT32_MAX; - for (unsigned i = 0; i < big_int->digit_count; i++) { - if (digits[i] != 0) { - last_non_zero = i; - } - } - if (last_non_zero == UINT32_MAX) { - big_int->is_negative = false; - big_int->digit_count = 0; - return; - } - big_int->digit_count = last_non_zero + 1; - if (!last_non_zero) { - big_int->digit = digits[0]; - } -} - -static char digit_to_char(uint8_t digit, bool upper) { - if (digit <= 9) { - return (char)(digit + '0'); - } - if (digit <= 35) { - return (char)(digit + (upper ? 'A' : 'a') - 10); - } - invalid_return; -} - -static bool bit_at_index(const BigInt *big_int, size_t index) { - size_t digit_index = index / 64; - if (digit_index >= big_int->digit_count) { - return false; - } - size_t digit_bit_index = index % 64; - const uint64_t *digits = bigint_ptr(big_int); - uint64_t digit = digits[digit_index]; - return ((digit >> digit_bit_index) & 0x1U) == 0x1U; -} - -uint32_t bigint_hash(BigInt x) { - if (x.digit_count == 0) return 0; - return (uint32_t)bigint_ptr(&x)[0]; -} - -static size_t bigint_bits_needed(const BigInt *big_int) { - size_t full_bits = big_int->digit_count * 64; - size_t leading_zero_count = bigint_clz(big_int, full_bits); - size_t bits_needed = full_bits - leading_zero_count; - return bits_needed + big_int->is_negative; -} - -void bigint_init_unsigned(BigInt *big_int, uint64_t value) { - if (value == 0) { - big_int->digit_count = 0; - big_int->is_negative = false; - return; - } - big_int->digit_count = 1; - big_int->digit = value; - big_int->is_negative = false; -} - -void bigint_init_signed(BigInt *dest, int64_t value) { - if (value >= 0) { - return bigint_init_unsigned(dest, (uint64_t)value); - } - dest->is_negative = true; - dest->digit_count = 1; - dest->digit = ((uint64_t)(-(value + 1))) + 1; -} - -void bigint_init_bigint(BigInt *dest, const BigInt *src) { - if (src->digit_count == 0) { - return bigint_init_unsigned(dest, 0); - } - if (src->digit_count == 1) { - dest->digit_count = 1; - dest->digit = src->digit; - dest->is_negative = src->is_negative; - return; - } - dest->is_negative = src->is_negative; - dest->digit_count = src->digit_count; - dest->digits = ALLOC_DIGITS(dest->digit_count); - memory_copy(dest->digits, src->digits, sizeof(uint64_t) * dest->digit_count); -} - -void bigint_negate(BigInt *dest, const BigInt *source) { - bigint_init_bigint(dest, source); - dest->is_negative = !dest->is_negative; - normalize(dest); -} - -static void to_twos_complement(BigInt *dest, const BigInt *source, size_t bit_count) { - if (bit_count == 0 || source->digit_count == 0) { - bigint_init_unsigned(dest, 0); - return; - } - if (source->is_negative) { - BigInt negated = {0}; - bigint_negate(&negated, source); - - BigInt inverted = {0}; - bigint_not(&inverted, &negated, bit_count, false); - - BigInt one = {0}; - bigint_init_unsigned(&one, 1); - - bigint_add(dest, &inverted, &one); - return; - } - - dest->is_negative = false; - const uint64_t *source_digits = bigint_ptr(source); - if (source->digit_count == 1) { - dest->digit = source_digits[0]; - if (bit_count < 64) { - dest->digit &= (1ULL << bit_count) - 1; - } - dest->digit_count = 1; - normalize(dest); - return; - } - unsigned digits_to_copy = (unsigned int)(bit_count / 64); - unsigned leftover_bits = (unsigned int)(bit_count % 64); - dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); - if (dest->digit_count == 1 && leftover_bits == 0) { - dest->digit = source_digits[0]; - if (dest->digit == 0) dest->digit_count = 0; - return; - } - dest->digits = (uint64_t *)malloc_arena(dest->digit_count * sizeof(uint64_t)); - for (size_t i = 0; i < digits_to_copy; i += 1) { - uint64_t digit = (i < source->digit_count) ? source_digits[i] : 0; - dest->digits[i] = digit; - } - if (leftover_bits != 0) { - uint64_t digit = (digits_to_copy < source->digit_count) ? source_digits[digits_to_copy] : 0; - dest->digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1); - } - normalize(dest); -} - -size_t bigint_clz(const BigInt *big_int, size_t bit_count) { - if (big_int->is_negative || bit_count == 0) { - return 0; - } - if (big_int->digit_count == 0) { - return bit_count; - } - size_t count = 0; - for (size_t i = bit_count - 1;;) { - if (bit_at_index(big_int, i)) { - return count; - } - count++; - if (i == 0) break; - i--; - } - return count; -} - -bool bigint_eql(BigInt a, BigInt b) { - return bigint_cmp(&a, &b) == CMP_EQ; -} - -static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) { - assert(!src->is_negative); - - if (bit_count == 0 || src->digit_count == 0) { - bigint_init_unsigned(dest, 0); - return; - } - - if (is_signed && bit_at_index(src, bit_count - 1)) { - BigInt negative_one = {0}; - bigint_init_signed(&negative_one, -1); - - BigInt minus_one = {0}; - bigint_add(&minus_one, src, &negative_one); - - BigInt inverted = {0}; - bigint_not(&inverted, &minus_one, bit_count, false); - - bigint_negate(dest, &inverted); - return; - } - - bigint_init_bigint(dest, src); -} - -void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative) { - if (digit_count == 0) return bigint_init_unsigned(dest, 0); - - if (digit_count == 1) { - dest->digit_count = 1; - dest->digit = digits[0]; - dest->is_negative = is_negative; - normalize(dest); - return; - } - - dest->digit_count = digit_count; - dest->is_negative = is_negative; - dest->digits = ALLOC_DIGITS(digit_count); - memory_copy(dest->digits, digits, sizeof(uint64_t) * digit_count); - - normalize(dest); -} - -/* TODO -void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) { - float128_t zero; - ui32_to_f128M(0, &zero); - - dest->is_negative = f128M_lt(&op->value, &zero); - float128_t abs_val; - if (dest->is_negative) { - f128M_sub(&zero, &op->value, &abs_val); - } else { - memory_copy(&abs_val, &op->value, sizeof(float128_t)); - } - - float128_t max_u64; - ui64_to_f128M(UINT64_MAX, &max_u64); - if (f128M_le(&abs_val, &max_u64)) { - dest->digit_count = 1; - dest->data.digit = f128M_to_ui64(&op->value, softfloat_round_minMag, false); - bigint_normalize(dest); - return; - } - - float128_t amt; - f128M_div(&abs_val, &max_u64, &amt); - float128_t remainder; - f128M_rem(&abs_val, &max_u64, &remainder); - - dest->digit_count = 2; - dest->data.digits = allocate_nonzero(dest->digit_count); - dest->data.digits[0] = f128M_to_ui64(&remainder, softfloat_round_minMag, false); - dest->data.digits[1] = f128M_to_ui64(&amt, softfloat_round_minMag, false); - bigint_normalize(dest); -} -*/ - -bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed) { - assert(big_int->digit_count != 1 || big_int->digit != 0); - if (bit_count == 0) { - return bigint_cmp_zero(big_int) == CMP_EQ; - } - if (big_int->digit_count == 0) { - return true; - } - - if (!is_signed) { - size_t full_bits = big_int->digit_count * 64; - size_t leading_zero_count = bigint_clz(big_int, full_bits); - return bit_count >= full_bits - leading_zero_count; - } - - BigInt one = {0}; - bigint_init_unsigned(&one, 1); - - BigInt shl_amt = {0}; - bigint_init_unsigned(&shl_amt, bit_count - 1); - - BigInt max_value_plus_one = {0}; - bigint_shl(&max_value_plus_one, &one, &shl_amt); - - BigInt max_value = {0}; - bigint_sub(&max_value, &max_value_plus_one, &one); - - BigInt min_value = {0}; - bigint_negate(&min_value, &max_value_plus_one); - - CmpRes min_cmp = bigint_cmp(big_int, &min_value); - CmpRes max_cmp = bigint_cmp(big_int, &max_value); - - return (min_cmp == CMP_GT || min_cmp == CMP_EQ) && (max_cmp == CMP_LT || max_cmp == CMP_EQ); -} - -void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) { - if (bit_count == 0) { - return; - } - - BigInt twos_comp = {0}; - to_twos_complement(&twos_comp, big_int, bit_count); - - const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp); - - size_t bits_in_last_digit = bit_count % 64; - if (bits_in_last_digit == 0) bits_in_last_digit = 64; - size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; - size_t unwritten_byte_count = 8 - bytes_in_last_digit; - - if (is_big_endian) { - size_t last_digit_index = (bit_count - 1) / 64; - size_t digit_index = last_digit_index; - size_t buf_index = 0; - for (;;) { - uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; - - for (size_t byte_index = 7;;) { - uint8_t byte = (uint8_t)(x & 0xffU); - if (digit_index == last_digit_index) { - buf[buf_index + byte_index - unwritten_byte_count] = byte; - if (byte_index == unwritten_byte_count) break; - } - else { - buf[buf_index + byte_index] = byte; - } - - if (byte_index == 0) break; - byte_index -= 1; - x >>= 8U; - } - - if (digit_index == 0) break; - digit_index -= 1; - if (digit_index == last_digit_index) { - buf_index += bytes_in_last_digit; - } - else { - buf_index += 8; - } - } - } - else { - size_t digit_count = (bit_count + 63) / 64; - size_t buf_index = 0; - for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) { - uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; - - for (size_t byte_index = 0; - byte_index < 8 && (digit_index + 1 < digit_count || byte_index < bytes_in_last_digit); - byte_index += 1) { - uint8_t byte = (uint8_t)(x & 0xffU); - buf[buf_index] = byte; - buf_index += 1; - x >>= 8U; - } - } - } -} - -uint64_t bigint_as_unsigned(const BigInt *bigint) { - assert(!bigint->is_negative); - if (bigint->digit_count == 0) { - return 0; - } - if (bigint->digit_count != 1) { - FATAL_ERROR("Bigint exceeds u64"); - } - return bigint->digit; -} - -void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, - bool is_signed) { - if (bit_count == 0) { - bigint_init_unsigned(dest, 0); - return; - } - - dest->digit_count = (unsigned int)((bit_count + 63) / 64); - uint64_t *digits; - if (dest->digit_count == 1) { - digits = &dest->digit; - } - else { - digits = ALLOC_DIGITS(dest->digit_count); - dest->digits = digits; - } - - size_t bits_in_last_digit = bit_count % 64; - if (bits_in_last_digit == 0) { - bits_in_last_digit = 64; - } - size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; - size_t unread_byte_count = 8 - bytes_in_last_digit; - - if (is_big_endian) { - size_t buf_index = 0; - uint64_t digit = 0; - for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) { - uint8_t byte = buf[buf_index]; - buf_index += 1; - digit <<= 8U; - digit |= byte; - } - digits[dest->digit_count - 1] = digit; - for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) { - digit = 0; - for (size_t byte_index = 0; byte_index < 8; byte_index += 1) { - uint8_t byte = buf[buf_index]; - buf_index += 1; - digit <<= 8; - digit |= byte; - } - digits[dest->digit_count - 1 - digit_index] = digit; - } - } - else { - size_t buf_index = 0; - for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) { - uint64_t digit = 0; - size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8; - for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) { - uint64_t byte = buf[buf_index]; - buf_index += 1; - - digit |= byte << (8 * byte_index); - } - digits[digit_index] = digit; - } - } - - if (is_signed) { - normalize(dest); - BigInt tmp = {0}; - bigint_init_bigint(&tmp, dest); - from_twos_complement(dest, &tmp, bit_count, true); - } - else { - dest->is_negative = false; - normalize(dest); - } -} - -#if defined(_MSC_VER) - -static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { - *result = op1 + op2; - return *result < op1 || *result < op2; -} - -static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { - *result = op1 - op2; - return *result > op1; -} - -bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { - *result = op1 * op2; - - if (op1 == 0 || op2 == 0) return false; - if (op1 > UINT64_MAX / op2) return true; - if (op2 > UINT64_MAX / op1) return true; - return false; -} - -#else - -static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { - return __builtin_uaddll_overflow((unsigned long long)op1, (unsigned long long)op2, - (unsigned long long *)result); -} - -static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { - return __builtin_usubll_overflow((unsigned long long)op1, (unsigned long long)op2, - (unsigned long long *)result); -} - -bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { - return __builtin_umulll_overflow((unsigned long long)op1, (unsigned long long)op2, - (unsigned long long *)result); -} - -#endif - -// @! Big int: Maybe look into those bool operations? -#pragma warning(push) -#pragma warning(disable : 4804) - -void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) { - assert(dest != op1); - assert(dest != op2); - if (op1->digit_count == 0) { - return bigint_init_bigint(dest, op2); - } - if (op2->digit_count == 0) { - return bigint_init_bigint(dest, op1); - } - if (op1->is_negative == op2->is_negative) { - dest->is_negative = op1->is_negative; - - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - bool overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->digit); - if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) { - dest->digit_count = 1; - normalize(dest); - return; - } - unsigned i = 1; - uint64_t first_digit = dest->digit; - dest->digits = ALLOC_DIGITS(unsigned_max(op1->digit_count, op2->digit_count) + 1); - dest->digits[0] = first_digit; - - for (;;) { - bool found_digit = false; - uint64_t x = (uint64_t)overflow; - overflow = 0; - - if (i < op1->digit_count) { - found_digit = true; - uint64_t digit = op1_digits[i]; - overflow += add_u64_overflow(x, digit, &x); - } - - if (i < op2->digit_count) { - found_digit = true; - uint64_t digit = op2_digits[i]; - overflow += add_u64_overflow(x, digit, &x); - } - - dest->digits[i] = x; - i += 1; - - if (!found_digit) { - dest->digit_count = i; - normalize(dest); - return; - } - } - } - const BigInt *op_pos; - const BigInt *op_neg; - if (op1->is_negative) { - op_neg = op1; - op_pos = op2; - } - else { - op_pos = op1; - op_neg = op2; - } - - BigInt op_neg_abs = {0}; - bigint_negate(&op_neg_abs, op_neg); - const BigInt *bigger_op; - const BigInt *smaller_op; - switch (bigint_cmp(op_pos, &op_neg_abs)) { - case CMP_EQ: - bigint_init_unsigned(dest, 0); - return; - case CMP_LT: - bigger_op = &op_neg_abs; - smaller_op = op_pos; - dest->is_negative = true; - break; - case CMP_GT: - bigger_op = op_pos; - smaller_op = &op_neg_abs; - dest->is_negative = false; - break; - default: - FATAL_ERROR("UNREACHABLE"); - } - const uint64_t *bigger_op_digits = bigint_ptr(bigger_op); - const uint64_t *smaller_op_digits = bigint_ptr(smaller_op); - uint64_t overflow = (uint64_t)sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->digit); - if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) { - dest->digit_count = 1; - normalize(dest); - return; - } - uint64_t first_digit = dest->digit; - dest->digits = ALLOC_DIGITS(bigger_op->digit_count); - dest->digits[0] = first_digit; - unsigned i = 1; - - for (;;) { - bool found_digit = false; - uint64_t x = bigger_op_digits[i]; - uint64_t prev_overflow = overflow; - overflow = 0; - - if (i < smaller_op->digit_count) { - found_digit = true; - uint64_t digit = smaller_op_digits[i]; - overflow += sub_u64_overflow(x, digit, &x); - } - if (sub_u64_overflow(x, prev_overflow, &x)) { - found_digit = true; - overflow += 1; - } - dest->digits[i] = x; - i += 1; - - if (!found_digit || i >= bigger_op->digit_count) { - break; - } - } - assert(overflow == 0); - dest->digit_count = i; - normalize(dest); -} -#pragma warning(pop) - -void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { - BigInt unwrapped = {0}; - bigint_add(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); -} - -void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) { - BigInt op2_negated = {0}; - bigint_negate(&op2_negated, op2); - return bigint_add(dest, op1, &op2_negated); -} - -void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { - BigInt op2_negated = {0}; - bigint_negate(&op2_negated, op2); - return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed); -} - -static void mul_overflow(uint64_t op1, uint64_t op2, uint64_t *lo, uint64_t *hi) { - uint64_t u1 = (op1 & 0xffffffff); - uint64_t v1 = (op2 & 0xffffffff); - uint64_t t = (u1 * v1); - uint64_t w3 = (t & 0xffffffff); - uint64_t k = (t >> 32); - - op1 >>= 32; - t = (op1 * v1) + k; - k = (t & 0xffffffff); - uint64_t w1 = (t >> 32); - - op2 >>= 32; - t = (u1 * op2) + k; - k = (t >> 32); - - *hi = (op1 * op2) + w1 + k; - *lo = (t << 32) + w3; -} - -static void mul_scalar(BigInt *dest, const BigInt *op, uint64_t scalar) { - bigint_init_unsigned(dest, 0); - - BigInt bi_64; - bigint_init_unsigned(&bi_64, 64); - - const uint64_t *op_digits = bigint_ptr(op); - size_t i = op->digit_count - 1; - - while (1) { - BigInt shifted; - bigint_shl(&shifted, dest, &bi_64); - - uint64_t result_scalar; - uint64_t carry_scalar; - mul_overflow(scalar, op_digits[i], &result_scalar, &carry_scalar); - - BigInt result; - bigint_init_unsigned(&result, result_scalar); - - BigInt carry; - bigint_init_unsigned(&carry, carry_scalar); - - BigInt carry_shifted; - bigint_shl(&carry_shifted, &carry, &bi_64); - - BigInt tmp; - bigint_add(&tmp, &shifted, &carry_shifted); - - bigint_add(dest, &tmp, &result); - - if (i == 0) { - break; - } - i -= 1; - } -} - -void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) { - assert(dest != op1); - assert(dest != op2); - if (op1->digit_count == 0 || op2->digit_count == 0) { - return bigint_init_unsigned(dest, 0); - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - - uint64_t carry; - mul_overflow(op1_digits[0], op2_digits[0], &dest->digit, &carry); - if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) { - dest->is_negative = (op1->is_negative != op2->is_negative); - dest->digit_count = 1; - normalize(dest); - return; - } - - bigint_init_unsigned(dest, 0); - - BigInt bi_64; - bigint_init_unsigned(&bi_64, 64); - - size_t i = op2->digit_count - 1; - for (;;) { - BigInt shifted; - bigint_shl(&shifted, dest, &bi_64); - - BigInt scalar_result; - mul_scalar(&scalar_result, op1, op2_digits[i]); - - bigint_add(dest, &scalar_result, &shifted); - - if (i == 0) { - break; - } - i -= 1; - } - - dest->is_negative = (op1->is_negative != op2->is_negative); - normalize(dest); -} - -void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { - BigInt unwrapped = {0}; - bigint_mul(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); -} - -unsigned count_leading_zeros(uint32_t val) { - if (val == 0) return 32; - -#if defined(_MSC_VER) - unsigned long Index; - _BitScanReverse(&Index, val); - return Index ^ 31; -#else - return __builtin_clz(val); -#endif -} - -/// Make a 64-bit integer from a high / low pair of 32-bit integers. -static inline uint64_t make_64(uint32_t hi, uint32_t lo) { - return (((uint64_t)hi) << 32) | ((uint64_t)lo); -} - -/// Return the high 32 bits of a 64 bit value. -static inline uint32_t hi_32(uint64_t value) { - return (uint32_t)(value >> 32); -} - -/// Return the low 32 bits of a 64 bit value. -static inline uint32_t lo_32(uint64_t val) { - return (uint32_t)val; -} - -/// Implementation of Knuth's Algorithm D (Division of nonnegative integers) -/// from "Art of Computer Programming, Volume 2", section 4.3.1, p. 272. The -/// variables here have the same names as in the algorithm. Comments explain -/// the algorithm and any deviation from it. -static void knuth_div(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t *r, unsigned m, unsigned n) { - assert(u && "Must provide dividend"); - assert(v && "Must provide divisor"); - assert(q && "Must provide quotient"); - assert(u != v && u != q && v != q && "Must use different memory"); - assert(n > 1 && "n must be > 1"); - - // b denotes the base of the number system. In our case b is 2^32. - const uint64_t b = ((uint64_t)1) << 32; - - // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of - // u and v by d. Note that we have taken Knuth's advice here to use a power - // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of - // 2 allows us to shift instead of multiply and it is easy to determine the - // shift amount from the leading zeros. We are basically normalizing the u - // and v so that its high bits are shifted to the top of v's range without - // overflow. Note that this can require an extra word in u so that u must - // be of length m+n+1. - unsigned shift = count_leading_zeros(v[n - 1]); - uint32_t v_carry = 0; - uint32_t u_carry = 0; - if (shift) { - for (unsigned i = 0; i < m + n; ++i) { - uint32_t u_tmp = u[i] >> (32 - shift); - u[i] = (u[i] << shift) | u_carry; - u_carry = u_tmp; - } - for (unsigned i = 0; i < n; ++i) { - uint32_t v_tmp = v[i] >> (32 - shift); - v[i] = (v[i] << shift) | v_carry; - v_carry = v_tmp; - } - } - u[m + n] = u_carry; - - // D2. [Initialize j.] Set j to m. This is the loop counter over the places. - int j = (int)m; - do { - // D3. [Calculate q'.]. - // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q') - // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r') - // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease - // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test - // on v[n-2] determines at high speed most of the cases in which the trial - // value qp is one too large, and it eliminates all cases where qp is two - // too large. - uint64_t dividend = make_64(u[j + n], u[j + n - 1]); - uint64_t qp = dividend / v[n - 1]; - uint64_t rp = dividend % v[n - 1]; - if (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2]) { - qp--; - rp += v[n - 1]; - if (rp < b && (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2])) { - qp--; - } - } - - // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with - // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation - // consists of a simple multiplication by a one-place number, combined with - // a subtraction. - // The digits (u[j+n]...u[j]) should be kept positive; if the result of - // this step is actually negative, (u[j+n]...u[j]) should be left as the - // true value plus b**(n+1), namely as the b's complement of - // the true value, and a "borrow" to the left should be remembered. - int64_t borrow = 0; - for (unsigned i = 0; i < n; ++i) { - uint64_t p = ((uint64_t)qp) * ((uint64_t)(v[i])); - int64_t subres = ((int64_t)(u[j + i])) - borrow - lo_32(p); - u[j + i] = lo_32((uint64_t)subres); - borrow = hi_32(p) - hi_32((uint64_t)subres); - } - bool is_neg = u[j + n] < borrow; - u[j + n] -= lo_32((uint64_t)borrow); - - // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was - // negative, go to step D6; otherwise go on to step D7. - q[j] = lo_32(qp); - if (is_neg) { - // D6. [Add back]. The probability that this step is necessary is very - // small, on the order of only 2/b. Make sure that test data accounts for - // this possibility. Decrease q[j] by 1 - q[j]--; - // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]). - // A carry will occur to the left of u[j+n], and it should be ignored - // since it cancels with the borrow that occurred in D4. - bool carry = false; - for (unsigned i = 0; i < n; i++) { - uint32_t limit = u32_min(u[j + i], v[i]); - u[j + i] += v[i] + carry; - carry = u[j + i] < limit || (carry && u[j + i] == limit); - } - u[j + n] += carry; - } - - // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3. - } while (--j >= 0); - - // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired - // remainder may be obtained by dividing u[...] by d. If r is non-null we - // compute the remainder (urem uses this). - if (r) { - // The value d is expressed by the "shift" value above since we avoided - // multiplication by d by using a shift left. So, all we have to do is - // shift right here. - if (shift) { - uint32_t carry = 0; - for (int i = (int)n - 1; i >= 0; i--) { - r[i] = (u[i] >> shift) | carry; - carry = u[i] << (32 - shift); - } - } - else { - for (int i = (int)n - 1; i >= 0; i--) { - r[i] = u[i]; - } - } - } -} - -static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) { - CmpRes cmp = bigint_cmp(op1, op2); - if (cmp == CMP_LT) { - if (!Quotient) { - bigint_init_unsigned(Quotient, 0); - } - if (!Remainder) { - bigint_init_bigint(Remainder, op1); - } - return; - } - if (cmp == CMP_EQ) { - if (!Quotient) { - bigint_init_unsigned(Quotient, 1); - } - if (!Remainder) { - bigint_init_unsigned(Remainder, 0); - } - return; - } - - const uint64_t *lhs = bigint_ptr(op1); - const uint64_t *rhs = bigint_ptr(op2); - unsigned lhsWords = op1->digit_count; - unsigned rhsWords = op2->digit_count; - - // First, compose the values into an array of 32-bit words instead of - // 64-bit words. This is a necessity of both the "short division" algorithm - // and the Knuth "classical algorithm" which requires there to be native - // operations for +, -, and * on an m bit value with an m*2 bit result. We - // can't use 64-bit operands here because we don't have native results of - // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't - // work on large-endian machines. - unsigned n = rhsWords * 2; - unsigned m = (lhsWords * 2) - n; - - // Allocate space for the temporary values we need either on the stack, if - // it will fit, or on the heap if it won't. - uint32_t space[128]; - uint32_t *U = NULL; - uint32_t *V = NULL; - uint32_t *Q = NULL; - uint32_t *R = NULL; - if ((Remainder ? 4 : 3) * n + 2 * m + 1 <= 128) { - U = &space[0]; - V = &space[m + n + 1]; - Q = &space[(m + n + 1) + n]; - if (Remainder) { - R = &space[(m + n + 1) + n + (m + n)]; - } - } - else { - U = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n + 1)); - V = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); - Q = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n)); - if (Remainder) { - R = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); - } - } - - // Initialize the dividend - memset(U, 0, (m + n + 1) * sizeof(uint32_t)); - for (unsigned i = 0; i < lhsWords; ++i) { - uint64_t tmp = lhs[i]; - U[i * 2] = lo_32(tmp); - U[i * 2 + 1] = hi_32(tmp); - } - U[m + n] = 0; // this extra word is for "spill" in the Knuth algorithm. - - // Initialize the divisor - memset(V, 0, (n) * sizeof(uint32_t)); - for (unsigned i = 0; i < rhsWords; ++i) { - uint64_t tmp = rhs[i]; - V[i * 2] = lo_32(tmp); - V[i * 2 + 1] = hi_32(tmp); - } - - // initialize the quotient and remainder - memset(Q, 0, (m + n) * sizeof(uint32_t)); - if (Remainder) memset(R, 0, n * sizeof(uint32_t)); - - // Now, adjust m and n for the Knuth division. n is the number of words in - // the divisor. m is the number of words by which the dividend exceeds the - // divisor (i.e. m+n is the length of the dividend). These sizes must not - // contain any zero words or the Knuth algorithm fails. - for (unsigned i = n; i > 0 && V[i - 1] == 0; i--) { - n--; - m++; - } - for (unsigned i = m + n; i > 0 && U[i - 1] == 0; i--) { - m--; - } - - // If we're left with only a single word for the divisor, Knuth doesn't work - // so we implement the short division algorithm here. This is much simpler - // and faster because we are certain that we can divide a 64-bit quantity - // by a 32-bit quantity at hardware speed and short division is simply a - // series of such operations. This is just like doing short division but we - // are using base 2^32 instead of base 10. - assert(n != 0 && "Divide by zero?"); - if (n == 1) { - uint32_t divisor = V[0]; - uint32_t rem = 0; - for (int i = (int)m; i >= 0; i--) { - uint64_t partial_dividend = make_64(rem, U[i]); - if (partial_dividend == 0) { - Q[i] = 0; - rem = 0; - } - else if (partial_dividend < divisor) { - Q[i] = 0; - rem = lo_32(partial_dividend); - } - else if (partial_dividend == divisor) { - Q[i] = 1; - rem = 0; - } - else { - Q[i] = lo_32(partial_dividend / divisor); - rem = lo_32(partial_dividend - (Q[i] * divisor)); - } - } - if (R) { - R[0] = rem; - } - } - else { - // Now we're ready to invoke the Knuth classical divide algorithm. In this - // case n > 1. - knuth_div(U, V, Q, R, m, n); - } - - // If the caller wants the quotient - if (Quotient) { - Quotient->is_negative = false; - Quotient->digit_count = lhsWords; - if (lhsWords == 1) { - Quotient->digit = make_64(Q[1], Q[0]); - } - else { - Quotient->digits = ALLOC_DIGITS(lhsWords); - for (size_t i = 0; i < lhsWords; i += 1) { - Quotient->digits[i] = make_64(Q[i * 2 + 1], Q[i * 2]); - } - } - } - - // If the caller wants the remainder - if (Remainder) { - Remainder->is_negative = false; - Remainder->digit_count = rhsWords; - if (rhsWords == 1) { - Remainder->digit = make_64(R[1], R[0]); - } - else { - Remainder->digits = ALLOC_DIGITS(rhsWords); - for (size_t i = 0; i < rhsWords; i += 1) { - Remainder->digits[i] = make_64(R[i * 2 + 1], R[i * 2]); - } - } - } -} - -void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) { - assert(op2->digit_count != 0); // division by zero - if (op1->digit_count == 0) { - bigint_init_unsigned(dest, 0); - return; - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) { - dest->digit = op1_digits[0] / op2_digits[0]; - dest->digit_count = 1; - dest->is_negative = op1->is_negative != op2->is_negative; - normalize(dest); - return; - } - if (op2->digit_count == 1 && op2_digits[0] == 1) { - // X / 1 == X - bigint_init_bigint(dest, op1); - dest->is_negative = op1->is_negative != op2->is_negative; - normalize(dest); - return; - } - - const BigInt *op1_positive; - BigInt op1_positive_data; - if (op1->is_negative) { - bigint_negate(&op1_positive_data, op1); - op1_positive = &op1_positive_data; - } - else { - op1_positive = op1; - } - - const BigInt *op2_positive; - BigInt op2_positive_data; - if (op2->is_negative) { - bigint_negate(&op2_positive_data, op2); - op2_positive = &op2_positive_data; - } - else { - op2_positive = op2; - } - - bigint_unsigned_division(op1_positive, op2_positive, dest, NULL); - dest->is_negative = op1->is_negative != op2->is_negative; - normalize(dest); -} - -void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) { - if (op1->is_negative != op2->is_negative) { - bigint_div_trunc(dest, op1, op2); - BigInt mult_again = {0}; - bigint_mul(&mult_again, dest, op2); - mult_again.is_negative = op1->is_negative; - if (bigint_cmp(&mult_again, op1) != CMP_EQ) { - BigInt tmp = {0}; - bigint_init_bigint(&tmp, dest); - BigInt neg_one = {0}; - bigint_init_signed(&neg_one, -1); - bigint_add(dest, &tmp, &neg_one); - } - normalize(dest); - } - else { - bigint_div_trunc(dest, op1, op2); - } -} - -void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) { - assert(op2->digit_count != 0); // division by zero - if (op1->digit_count == 0) { - bigint_init_unsigned(dest, 0); - return; - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - - if (op1->digit_count == 1 && op2->digit_count == 1) { - dest->digit = op1_digits[0] % op2_digits[0]; - dest->digit_count = 1; - dest->is_negative = op1->is_negative; - normalize(dest); - return; - } - if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) { - // special case this divisor - bigint_init_unsigned(dest, op1_digits[0]); - dest->is_negative = op1->is_negative; - normalize(dest); - return; - } - - if (op2->digit_count == 1 && op2_digits[0] == 1) { - // X % 1 == 0 - bigint_init_unsigned(dest, 0); - return; - } - - const BigInt *op1_positive; - BigInt op1_positive_data; - if (op1->is_negative) { - bigint_negate(&op1_positive_data, op1); - op1_positive = &op1_positive_data; - } - else { - op1_positive = op1; - } - - const BigInt *op2_positive; - BigInt op2_positive_data; - if (op2->is_negative) { - bigint_negate(&op2_positive_data, op2); - op2_positive = &op2_positive_data; - } - else { - op2_positive = op2; - } - - bigint_unsigned_division(op1_positive, op2_positive, NULL, dest); - dest->is_negative = op1->is_negative; - normalize(dest); -} - -void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) { - if (op1->is_negative) { - BigInt first_rem; - bigint_rem(&first_rem, op1, op2); - first_rem.is_negative = !op2->is_negative; - BigInt op2_minus_rem; - bigint_add(&op2_minus_rem, op2, &first_rem); - bigint_rem(dest, &op2_minus_rem, op2); - dest->is_negative = false; - } - else { - bigint_rem(dest, op1, op2); - dest->is_negative = false; - } -} - -void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) { - if (op1->digit_count == 0) { - return bigint_init_bigint(dest, op2); - } - if (op2->digit_count == 0) { - return bigint_init_bigint(dest, op1); - } - if (op1->is_negative || op2->is_negative) { - size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - - BigInt twos_comp_op1 = {0}; - to_twos_complement(&twos_comp_op1, op1, big_bit_count); - - BigInt twos_comp_op2 = {0}; - to_twos_complement(&twos_comp_op2, op2, big_bit_count); - - BigInt twos_comp_dest = {0}; - bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); - - from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); - } - else { - dest->is_negative = false; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) { - dest->digit_count = 1; - dest->digit = op1_digits[0] | op2_digits[0]; - normalize(dest); - return; - } - dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - for (size_t i = 0; i < dest->digit_count; i += 1) { - uint64_t digit = 0; - if (i < op1->digit_count) { - digit |= op1_digits[i]; - } - if (i < op2->digit_count) { - digit |= op2_digits[i]; - } - dest->digits[i] = digit; - } - normalize(dest); - } -} - -void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) { - if (op1->digit_count == 0 || op2->digit_count == 0) { - return bigint_init_unsigned(dest, 0); - } - if (op1->is_negative || op2->is_negative) { - size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - - BigInt twos_comp_op1 = {0}; - to_twos_complement(&twos_comp_op1, op1, big_bit_count); - - BigInt twos_comp_op2 = {0}; - to_twos_complement(&twos_comp_op2, op2, big_bit_count); - - BigInt twos_comp_dest = {0}; - bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); - - from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); - } - else { - dest->is_negative = false; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - if (op1->digit_count == 1 && op2->digit_count == 1) { - dest->digit_count = 1; - dest->digit = op1_digits[0] & op2_digits[0]; - normalize(dest); - return; - } - - dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - - size_t i = 0; - for (; i < op1->digit_count && i < op2->digit_count; i += 1) { - dest->digits[i] = op1_digits[i] & op2_digits[i]; - } - for (; i < dest->digit_count; i += 1) { - dest->digits[i] = 0; - } - normalize(dest); - } -} - -void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) { - if (op1->digit_count == 0) { - return bigint_init_bigint(dest, op2); - } - if (op2->digit_count == 0) { - return bigint_init_bigint(dest, op1); - } - if (op1->is_negative || op2->is_negative) { - size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); - - BigInt twos_comp_op1 = {0}; - to_twos_complement(&twos_comp_op1, op1, big_bit_count); - - BigInt twos_comp_op2 = {0}; - to_twos_complement(&twos_comp_op2, op2, big_bit_count); - - BigInt twos_comp_dest = {0}; - bigint_xor(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); - - from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); - } - else { - dest->is_negative = false; - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - - assert(op1->digit_count > 0 && op2->digit_count > 0); - if (op1->digit_count == 1 && op2->digit_count == 1) { - dest->digit_count = 1; - dest->digit = op1_digits[0] ^ op2_digits[0]; - normalize(dest); - return; - } - dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - size_t i = 0; - for (; i < op1->digit_count && i < op2->digit_count; i += 1) { - dest->digits[i] = op1_digits[i] ^ op2_digits[i]; - } - for (; i < dest->digit_count; i += 1) { - if (i < op1->digit_count) { - dest->digits[i] = op1_digits[i]; - } - else if (i < op2->digit_count) { - dest->digits[i] = op2_digits[i]; - } - else { - FATAL_ERROR("Unreachable"); - } - } - normalize(dest); - } -} - -void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift); -void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) { - assert(!op2->is_negative); - - if (op2->digit_count != 1) { - FATAL_ERROR("Unsupported: shift left by amount greater than 64 bit integer"); - } - bigint_shl_int(dest, op1, bigint_as_unsigned(op2)); -} - -void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift) { - if (shift == 0) { - bigint_init_bigint(dest, op1); - return; - } - - if (op1->digit_count == 0) { - bigint_init_unsigned(dest, 0); - return; - } - - const uint64_t *op1_digits = bigint_ptr(op1); - - if (op1->digit_count == 1 && shift < 64) { - dest->digit = op1_digits[0] << shift; - if (dest->digit > op1_digits[0]) { - dest->digit_count = 1; - dest->is_negative = op1->is_negative; - return; - } - } - - uint64_t digit_shift_count = shift / 64; - uint64_t leftover_shift_count = shift % 64; - - dest->digits = ALLOC_DIGITS(op1->digit_count + digit_shift_count + 1); - dest->digit_count = (unsigned)digit_shift_count; - uint64_t carry = 0; - for (size_t i = 0; i < op1->digit_count; i += 1) { - uint64_t digit = op1_digits[i]; - dest->digits[dest->digit_count] = carry | (digit << leftover_shift_count); - dest->digit_count++; - if (leftover_shift_count > 0) { - carry = digit >> (64 - leftover_shift_count); - } - else { - carry = 0; - } - } - dest->digits[dest->digit_count] = carry; - dest->digit_count += 1; - dest->is_negative = op1->is_negative; - normalize(dest); -} - -void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { - BigInt unwrapped = {0}; - bigint_shl(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); -} - -void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { - assert(!op2->is_negative); - - if (op1->digit_count == 0) { - return bigint_init_unsigned(dest, 0); - } - - if (op2->digit_count == 0) { - return bigint_init_bigint(dest, op1); - } - - if (op2->digit_count != 1) { - FATAL_ERROR("Unsupported: shift right by amount greater than 64 bit integer"); - } - - const uint64_t *op1_digits = bigint_ptr(op1); - uint64_t shift_amt = bigint_as_unsigned(op2); - - if (op1->digit_count == 1) { - dest->digit = shift_amt < 64 ? op1_digits[0] >> shift_amt : 0; - dest->digit_count = 1; - dest->is_negative = op1->is_negative; - normalize(dest); - return; - } - - uint64_t digit_shift_count = shift_amt / 64; - uint64_t leftover_shift_count = shift_amt % 64; - - if (digit_shift_count >= op1->digit_count) { - return bigint_init_unsigned(dest, 0); - } - - dest->digit_count = (unsigned)(op1->digit_count - digit_shift_count); - uint64_t *digits; - if (dest->digit_count == 1) { - digits = &dest->digit; - } - else { - digits = ALLOC_DIGITS(dest->digit_count); - dest->digits = digits; - } - - uint64_t carry = 0; - for (size_t op_digit_index = op1->digit_count - 1;;) { - uint64_t digit = op1_digits[op_digit_index]; - size_t dest_digit_index = op_digit_index - digit_shift_count; - digits[dest_digit_index] = carry | (digit >> leftover_shift_count); - carry = digit << (64 - leftover_shift_count); - - if (dest_digit_index == 0) break; - op_digit_index -= 1; - } - dest->is_negative = op1->is_negative; - normalize(dest); -} - -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) { - BigInt zero; - bigint_init_unsigned(&zero, 0); - bigint_sub_wrap(dest, &zero, op, bit_count, true); -} - -void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { - if (bit_count == 0) { - bigint_init_unsigned(dest, 0); - return; - } - - if (is_signed) { - BigInt twos_comp = {0}; - to_twos_complement(&twos_comp, op, bit_count); - - BigInt inverted = {0}; - bigint_not(&inverted, &twos_comp, bit_count, false); - - from_twos_complement(dest, &inverted, bit_count, true); - return; - } - - assert(!op->is_negative); - - dest->is_negative = false; - const uint64_t *op_digits = bigint_ptr(op); - if (bit_count <= 64) { - dest->digit_count = 1; - if (op->digit_count == 0) { - if (bit_count == 64) { - dest->digit = UINT64_MAX; - } - else { - dest->digit = (1ULL << bit_count) - 1; - } - } - else if (op->digit_count == 1) { - dest->digit = ~op_digits[0]; - if (bit_count != 64) { - uint64_t - mask = (1ULL << bit_count) - 1; - dest->digit &= mask; - } - } - normalize(dest); - return; - } - dest->digit_count = (unsigned int)((bit_count + 63) / 64); - assert(dest->digit_count >= op->digit_count); - dest->digits = ALLOC_DIGITS(dest->digit_count); - size_t i = 0; - for (; i < op->digit_count; i += 1) { - dest->digits[i] = ~op_digits[i]; - } - for (; i < dest->digit_count; i += 1) { - dest->digits[i] = 0xffffffffffffffffULL; - } - size_t digit_index = dest->digit_count - 1; - size_t digit_bit_index = bit_count % 64; - if (digit_bit_index != 0) { - uint64_t - mask = (1ULL << digit_bit_index) - 1; - dest->digits[digit_index] &= mask; - } - normalize(dest); -} - -void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed) { - BigInt twos_comp; - to_twos_complement(&twos_comp, op, bit_count); - from_twos_complement(dst, &twos_comp, bit_count, is_signed); -} - -CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2) { - if (op1->is_negative && !op2->is_negative) return CMP_LT; - if (!op1->is_negative && op2->is_negative) return CMP_GT; - if (op1->digit_count > op2->digit_count) return op1->is_negative ? CMP_LT : CMP_GT; - if (op2->digit_count > op1->digit_count) return op1->is_negative ? CMP_GT : CMP_LT; - if (op1->digit_count == 0) return CMP_EQ; - - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - for (unsigned i = op1->digit_count - 1;; i--) { - uint64_t op1_digit = op1_digits[i]; - uint64_t op2_digit = op2_digits[i]; - - if (op1_digit > op2_digit) { - return op1->is_negative ? CMP_LT : CMP_GT; - } - if (op1_digit < op2_digit) { - return op1->is_negative ? CMP_GT : CMP_LT; - } - if (i == 0) { - return CMP_EQ; - } - } -} - -void bigint_print(BigInt *bigint, uint64_t base) { - if (bigint->digit_count == 0) { - printf("0"); - return; - } - if (bigint->is_negative) { - printf("-"); - } - if (bigint->digit_count == 1 && base == 10) { - printf("%" PRIu64, bigint->digit); - return; - } - size_t len = bigint->digit_count * 64; - char *start = (char *)malloc_arena(len); - char *buf = start; - - BigInt digit_bi = {0}; - BigInt a1 = {0}; - BigInt a2 = {0}; - - BigInt *a = &a1; - BigInt *other_a = &a2; - bigint_init_bigint(a, bigint); - - BigInt base_bi = {0}; - bigint_init_unsigned(&base_bi, base); - - for (;;) { - bigint_rem(&digit_bi, a, &base_bi); - uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); - *(buf++) = digit_to_char(digit, false); - bigint_div_trunc(other_a, a, &base_bi); - { - BigInt *tmp = a; - a = other_a; - other_a = tmp; - } - if (bigint_cmp_zero(a) == CMP_EQ) { - break; - } - } - - // reverse - - for (char *ptr = buf - 1; ptr >= start; ptr--) { - printf("%c", *ptr); - } -} - -const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, uint64_t base) { - Set_BigInt_Arena(allocator); - if (bigint->digit_count == 0) { - return "0"; - } - if (bigint->digit_count == 1 && base == 10) { - char *res = NULL; - if (bigint->is_negative) { - String string = string_fmt(allocator, "-%" PRIu64, bigint->digit); - return (const char *)string.str; - } - else { - String string = string_fmt(allocator, "%" PRIu64, bigint->digit); - return (const char *)string.str; - } - return res; - } - size_t len = bigint->digit_count * 64; - char *start = (char *)allocate_size(allocator, len); - char *buf = start; - - BigInt digit_bi = {0}; - BigInt a1 = {0}; - BigInt a2 = {0}; - - BigInt *a = &a1; - BigInt *other_a = &a2; - bigint_init_bigint(a, bigint); - - BigInt base_bi = {0}; - bigint_init_unsigned(&base_bi, base); - - for (;;) { - bigint_rem(&digit_bi, a, &base_bi); - uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); - *(buf++) = digit_to_char(digit, false); - bigint_div_trunc(other_a, a, &base_bi); - { - BigInt *tmp = a; - a = other_a; - other_a = tmp; - } - if (bigint_cmp_zero(a) == CMP_EQ) { - break; - } - } - - // reverse - char *out = (char *)allocate_size(allocator, buf - start + 2); - char *current = out; - if (bigint->is_negative) { - *(current++) = '-'; - } - for (char *ptr = buf - 1; ptr >= start; ptr--) { - *(current++) = *ptr; - } - *(current++) = '\0'; - - return out; -} - -void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base) { - if (bigint->digit_count == 0) { - fprintf(file, "0"); - return; - } - if (bigint->is_negative) { - fprintf(file, "-"); - } - if (bigint->digit_count == 1 && base == 10) { - fprintf(file, "%" PRIu64, bigint->digit); - return; - } - size_t len = bigint->digit_count * 64; - char *start = (char *)malloc_arena(len); - char *buf = start; - - BigInt digit_bi = {0}; - BigInt a1 = {0}; - BigInt a2 = {0}; - - BigInt *a = &a1; - BigInt *other_a = &a2; - bigint_init_bigint(a, bigint); - - BigInt base_bi = {0}; - bigint_init_unsigned(&base_bi, base); - - for (;;) { - bigint_rem(&digit_bi, a, &base_bi); - uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); - *(buf++) = digit_to_char(digit, false); - bigint_div_trunc(other_a, a, &base_bi); - { - BigInt *tmp = a; - a = other_a; - other_a = tmp; - } - if (bigint_cmp_zero(a) == CMP_EQ) { - break; - } - } - - // reverse - - for (char *ptr = buf - 1; ptr >= start; ptr--) { - fprintf(file, "%c", *ptr); - } -} -size_t bigint_popcount_unsigned(const BigInt *big_int) { - assert(!big_int->is_negative); - if (big_int->digit_count == 0) { - return 0; - } - - unsigned count = 0; - size_t bit_count = big_int->digit_count * 64; - for (size_t i = 0; i < bit_count; i++) { - if (bit_at_index(big_int, i)) { - count += 1; - } - } - return count; -} - -size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) { - if (bit_count == 0) { - return 0; - } - if (bi->digit_count == 0) { - return 0; - } - - BigInt twos_comp = {0}; - to_twos_complement(&twos_comp, bi, bit_count); - - size_t count = 0; - for (size_t i = 0; i < bit_count; i += 1) { - if (bit_at_index(&twos_comp, i)) { - count += 1; - } - } - return count; -} - -size_t bigint_ctz(const BigInt *bi, size_t bit_count) { - if (bit_count == 0) { - return 0; - } - if (bi->digit_count == 0) { - return bit_count; - } - - BigInt twos_comp = {0}; - to_twos_complement(&twos_comp, bi, bit_count); - - size_t count = 0; - for (size_t i = 0; i < bit_count; i += 1) { - if (bit_at_index(&twos_comp, i)) { - return count; - } - count += 1; - } - return count; -} - -int64_t bigint_as_signed(const BigInt *bigint) { - if (bigint->digit_count == 0) return 0; - if (bigint->digit_count != 1) { - FATAL_ERROR("BigInt larger than i64"); - } - - if (bigint->is_negative) { - // TODO this code path is untested - if (bigint->digit <= 9223372036854775808ULL) { - return (-((int64_t)(bigint->digit - 1))) - 1; - } - FATAL_ERROR("BigInt does not fit in i64"); - } - return (int64_t)bigint->digit; -} - -CmpRes bigint_cmp_zero(const BigInt *op) { - if (op->digit_count == 0) { - return CMP_EQ; - } - return op->is_negative ? CMP_LT : CMP_GT; -} - -void bigint_incr(BigInt *x) { - if (!x->digit_count) { - bigint_init_unsigned(x, 1); - return; - } - - if (x->digit_count == 1) { - if (x->is_negative && x->digit != 0) { - x->digit -= 1; - return; - } - if (!x->is_negative && x->digit != UINT64_MAX) { - x->digit += 1; - return; - } - } - - BigInt copy; - bigint_init_bigint(©, x); - - BigInt one; - bigint_init_unsigned(&one, 1); - - bigint_add(x, ©, &one); -} - -double bigint_as_float(const BigInt *bigint) { - if (bigint_fits_in_bits(bigint, 64, bigint->is_negative)) { - return bigint->is_negative ? (double)bigint_as_signed(bigint) : (double)bigint_as_unsigned(bigint); - } - BigInt div; - uint64_t mult = 0x100000000000ULL; - double mul = 1; - bigint_init_unsigned(&div, mult); - BigInt current; - bigint_init_bigint(¤t, bigint); - double f = 0; - do { - BigInt temp; - bigint_mod(&temp, ¤t, &div); - f += bigint_as_signed(&temp) * mul; - mul *= mult; - bigint_div_trunc(&temp, ¤t, &div); - current = temp; - } while (current.digit_count > 0); - return f; -} +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by the GNU LGPLv3.0 license +// a copy of which can be found in the LICENSE file. + +#define Set_BigInt_Arena(x) BigInt_Arena bigint_allocator(x) +struct BigInt_Arena { + Allocator *old; + BigInt_Arena(Allocator *allocator) { + old = bigint_allocator; + bigint_allocator = allocator; + } + ~BigInt_Arena() { bigint_allocator = old; } +}; + +CORE_Static BigInt +bigint_u64(U64 value) { + BigInt result; + bigint_init_unsigned(&result, value); + return result; +} + +CORE_Static BigInt +bigint_s64(S64 value) { + BigInt result; + bigint_init_signed(&result, value); + return result; +} + +CORE_Static BigInt +bigint_mul(const BigInt *a, const BigInt *b) { + BigInt result; + bigint_mul(&result, a, b); + return result; +} + +CORE_Static BigInt +bigint_add(const BigInt *a, const BigInt *b) { + BigInt result; + bigint_add(&result, a, b); + return result; +} + +CORE_Static BigInt +bigint_copy(Allocator *allocator, BigInt *src) { + BigInt dest = {}; + if (src->digit_count == 0) { + bigint_init_unsigned(&dest, 0); + return dest; + } + if (src->digit_count == 1) { + dest.digit_count = 1; + dest.digit = src->digit; + dest.is_negative = src->is_negative; + return dest; + } + dest.is_negative = src->is_negative; + dest.digit_count = src->digit_count; + + dest.digits = allocate_array(allocator, uint64_t, dest.digit_count); + memory_copy(dest.digits, src->digits, sizeof(uint64_t) * dest.digit_count); + return dest; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +static inline uint32_t u32_min(uint32_t a, uint32_t b) { + return a < b ? a : b; +} + +static inline size_t size_max(size_t a, size_t b) { + return a > b ? a : b; +} + +static inline unsigned unsigned_max(unsigned a, unsigned b) { + return a > b ? a : b; +} + +static inline const uint64_t *bigint_ptr(const BigInt *big_int) { + return big_int->digit_count == 1 ? &big_int->digit : big_int->digits; +} + +static void normalize(BigInt *big_int) { + const uint64_t *digits = bigint_ptr(big_int); + unsigned last_non_zero = UINT32_MAX; + for (unsigned i = 0; i < big_int->digit_count; i++) { + if (digits[i] != 0) { + last_non_zero = i; + } + } + if (last_non_zero == UINT32_MAX) { + big_int->is_negative = false; + big_int->digit_count = 0; + return; + } + big_int->digit_count = last_non_zero + 1; + if (!last_non_zero) { + big_int->digit = digits[0]; + } +} + +static char digit_to_char(uint8_t digit, bool upper) { + if (digit <= 9) { + return (char)(digit + '0'); + } + if (digit <= 35) { + return (char)(digit + (upper ? 'A' : 'a') - 10); + } + invalid_return; +} + +static bool bit_at_index(const BigInt *big_int, size_t index) { + size_t digit_index = index / 64; + if (digit_index >= big_int->digit_count) { + return false; + } + size_t digit_bit_index = index % 64; + const uint64_t *digits = bigint_ptr(big_int); + uint64_t digit = digits[digit_index]; + return ((digit >> digit_bit_index) & 0x1U) == 0x1U; +} + +uint32_t bigint_hash(BigInt x) { + if (x.digit_count == 0) return 0; + return (uint32_t)bigint_ptr(&x)[0]; +} + +static size_t bigint_bits_needed(const BigInt *big_int) { + size_t full_bits = big_int->digit_count * 64; + size_t leading_zero_count = bigint_clz(big_int, full_bits); + size_t bits_needed = full_bits - leading_zero_count; + return bits_needed + big_int->is_negative; +} + +void bigint_init_unsigned(BigInt *big_int, uint64_t value) { + if (value == 0) { + big_int->digit_count = 0; + big_int->is_negative = false; + return; + } + big_int->digit_count = 1; + big_int->digit = value; + big_int->is_negative = false; +} + +void bigint_init_signed(BigInt *dest, int64_t value) { + if (value >= 0) { + return bigint_init_unsigned(dest, (uint64_t)value); + } + dest->is_negative = true; + dest->digit_count = 1; + dest->digit = ((uint64_t)(-(value + 1))) + 1; +} + +void bigint_init_bigint(BigInt *dest, const BigInt *src) { + if (src->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } + if (src->digit_count == 1) { + dest->digit_count = 1; + dest->digit = src->digit; + dest->is_negative = src->is_negative; + return; + } + dest->is_negative = src->is_negative; + dest->digit_count = src->digit_count; + dest->digits = ALLOC_DIGITS(dest->digit_count); + memory_copy(dest->digits, src->digits, sizeof(uint64_t) * dest->digit_count); +} + +void bigint_negate(BigInt *dest, const BigInt *source) { + bigint_init_bigint(dest, source); + dest->is_negative = !dest->is_negative; + normalize(dest); +} + +static void to_twos_complement(BigInt *dest, const BigInt *source, size_t bit_count) { + if (bit_count == 0 || source->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + if (source->is_negative) { + BigInt negated = {0}; + bigint_negate(&negated, source); + + BigInt inverted = {0}; + bigint_not(&inverted, &negated, bit_count, false); + + BigInt one = {0}; + bigint_init_unsigned(&one, 1); + + bigint_add(dest, &inverted, &one); + return; + } + + dest->is_negative = false; + const uint64_t *source_digits = bigint_ptr(source); + if (source->digit_count == 1) { + dest->digit = source_digits[0]; + if (bit_count < 64) { + dest->digit &= (1ULL << bit_count) - 1; + } + dest->digit_count = 1; + normalize(dest); + return; + } + unsigned digits_to_copy = (unsigned int)(bit_count / 64); + unsigned leftover_bits = (unsigned int)(bit_count % 64); + dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); + if (dest->digit_count == 1 && leftover_bits == 0) { + dest->digit = source_digits[0]; + if (dest->digit == 0) dest->digit_count = 0; + return; + } + dest->digits = (uint64_t *)malloc_arena(dest->digit_count * sizeof(uint64_t)); + for (size_t i = 0; i < digits_to_copy; i += 1) { + uint64_t digit = (i < source->digit_count) ? source_digits[i] : 0; + dest->digits[i] = digit; + } + if (leftover_bits != 0) { + uint64_t digit = (digits_to_copy < source->digit_count) ? source_digits[digits_to_copy] : 0; + dest->digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1); + } + normalize(dest); +} + +size_t bigint_clz(const BigInt *big_int, size_t bit_count) { + if (big_int->is_negative || bit_count == 0) { + return 0; + } + if (big_int->digit_count == 0) { + return bit_count; + } + size_t count = 0; + for (size_t i = bit_count - 1;;) { + if (bit_at_index(big_int, i)) { + return count; + } + count++; + if (i == 0) break; + i--; + } + return count; +} + +bool bigint_eql(BigInt a, BigInt b) { + return bigint_cmp(&a, &b) == CMP_EQ; +} + +static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) { + assert(!src->is_negative); + + if (bit_count == 0 || src->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + if (is_signed && bit_at_index(src, bit_count - 1)) { + BigInt negative_one = {0}; + bigint_init_signed(&negative_one, -1); + + BigInt minus_one = {0}; + bigint_add(&minus_one, src, &negative_one); + + BigInt inverted = {0}; + bigint_not(&inverted, &minus_one, bit_count, false); + + bigint_negate(dest, &inverted); + return; + } + + bigint_init_bigint(dest, src); +} + +void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative) { + if (digit_count == 0) return bigint_init_unsigned(dest, 0); + + if (digit_count == 1) { + dest->digit_count = 1; + dest->digit = digits[0]; + dest->is_negative = is_negative; + normalize(dest); + return; + } + + dest->digit_count = digit_count; + dest->is_negative = is_negative; + dest->digits = ALLOC_DIGITS(digit_count); + memory_copy(dest->digits, digits, sizeof(uint64_t) * digit_count); + + normalize(dest); +} + +/* TODO +void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) { + float128_t zero; + ui32_to_f128M(0, &zero); + + dest->is_negative = f128M_lt(&op->value, &zero); + float128_t abs_val; + if (dest->is_negative) { + f128M_sub(&zero, &op->value, &abs_val); + } else { + memory_copy(&abs_val, &op->value, sizeof(float128_t)); + } + + float128_t max_u64; + ui64_to_f128M(UINT64_MAX, &max_u64); + if (f128M_le(&abs_val, &max_u64)) { + dest->digit_count = 1; + dest->data.digit = f128M_to_ui64(&op->value, softfloat_round_minMag, false); + bigint_normalize(dest); + return; + } + + float128_t amt; + f128M_div(&abs_val, &max_u64, &amt); + float128_t remainder; + f128M_rem(&abs_val, &max_u64, &remainder); + + dest->digit_count = 2; + dest->data.digits = allocate_nonzero(dest->digit_count); + dest->data.digits[0] = f128M_to_ui64(&remainder, softfloat_round_minMag, false); + dest->data.digits[1] = f128M_to_ui64(&amt, softfloat_round_minMag, false); + bigint_normalize(dest); +} +*/ + +bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed) { + assert(big_int->digit_count != 1 || big_int->digit != 0); + if (bit_count == 0) { + return bigint_cmp_zero(big_int) == CMP_EQ; + } + if (big_int->digit_count == 0) { + return true; + } + + if (!is_signed) { + size_t full_bits = big_int->digit_count * 64; + size_t leading_zero_count = bigint_clz(big_int, full_bits); + return bit_count >= full_bits - leading_zero_count; + } + + BigInt one = {0}; + bigint_init_unsigned(&one, 1); + + BigInt shl_amt = {0}; + bigint_init_unsigned(&shl_amt, bit_count - 1); + + BigInt max_value_plus_one = {0}; + bigint_shl(&max_value_plus_one, &one, &shl_amt); + + BigInt max_value = {0}; + bigint_sub(&max_value, &max_value_plus_one, &one); + + BigInt min_value = {0}; + bigint_negate(&min_value, &max_value_plus_one); + + CmpRes min_cmp = bigint_cmp(big_int, &min_value); + CmpRes max_cmp = bigint_cmp(big_int, &max_value); + + return (min_cmp == CMP_GT || min_cmp == CMP_EQ) && (max_cmp == CMP_LT || max_cmp == CMP_EQ); +} + +void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) { + if (bit_count == 0) { + return; + } + + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, big_int, bit_count); + + const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp); + + size_t bits_in_last_digit = bit_count % 64; + if (bits_in_last_digit == 0) bits_in_last_digit = 64; + size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; + size_t unwritten_byte_count = 8 - bytes_in_last_digit; + + if (is_big_endian) { + size_t last_digit_index = (bit_count - 1) / 64; + size_t digit_index = last_digit_index; + size_t buf_index = 0; + for (;;) { + uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; + + for (size_t byte_index = 7;;) { + uint8_t byte = (uint8_t)(x & 0xffU); + if (digit_index == last_digit_index) { + buf[buf_index + byte_index - unwritten_byte_count] = byte; + if (byte_index == unwritten_byte_count) break; + } + else { + buf[buf_index + byte_index] = byte; + } + + if (byte_index == 0) break; + byte_index -= 1; + x >>= 8U; + } + + if (digit_index == 0) break; + digit_index -= 1; + if (digit_index == last_digit_index) { + buf_index += bytes_in_last_digit; + } + else { + buf_index += 8; + } + } + } + else { + size_t digit_count = (bit_count + 63) / 64; + size_t buf_index = 0; + for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) { + uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; + + for (size_t byte_index = 0; + byte_index < 8 && (digit_index + 1 < digit_count || byte_index < bytes_in_last_digit); + byte_index += 1) { + uint8_t byte = (uint8_t)(x & 0xffU); + buf[buf_index] = byte; + buf_index += 1; + x >>= 8U; + } + } + } +} + +uint64_t bigint_as_unsigned(const BigInt *bigint) { + assert(!bigint->is_negative); + if (bigint->digit_count == 0) { + return 0; + } + if (bigint->digit_count != 1) { + FATAL_ERROR("Bigint exceeds u64"); + } + return bigint->digit; +} + +void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, + bool is_signed) { + if (bit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + dest->digit_count = (unsigned int)((bit_count + 63) / 64); + uint64_t *digits; + if (dest->digit_count == 1) { + digits = &dest->digit; + } + else { + digits = ALLOC_DIGITS(dest->digit_count); + dest->digits = digits; + } + + size_t bits_in_last_digit = bit_count % 64; + if (bits_in_last_digit == 0) { + bits_in_last_digit = 64; + } + size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; + size_t unread_byte_count = 8 - bytes_in_last_digit; + + if (is_big_endian) { + size_t buf_index = 0; + uint64_t digit = 0; + for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) { + uint8_t byte = buf[buf_index]; + buf_index += 1; + digit <<= 8U; + digit |= byte; + } + digits[dest->digit_count - 1] = digit; + for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) { + digit = 0; + for (size_t byte_index = 0; byte_index < 8; byte_index += 1) { + uint8_t byte = buf[buf_index]; + buf_index += 1; + digit <<= 8; + digit |= byte; + } + digits[dest->digit_count - 1 - digit_index] = digit; + } + } + else { + size_t buf_index = 0; + for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) { + uint64_t digit = 0; + size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8; + for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) { + uint64_t byte = buf[buf_index]; + buf_index += 1; + + digit |= byte << (8 * byte_index); + } + digits[digit_index] = digit; + } + } + + if (is_signed) { + normalize(dest); + BigInt tmp = {0}; + bigint_init_bigint(&tmp, dest); + from_twos_complement(dest, &tmp, bit_count, true); + } + else { + dest->is_negative = false; + normalize(dest); + } +} + +#if defined(_MSC_VER) + +static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + *result = op1 + op2; + return *result < op1 || *result < op2; +} + +static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + *result = op1 - op2; + return *result > op1; +} + +bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + *result = op1 * op2; + + if (op1 == 0 || op2 == 0) return false; + if (op1 > UINT64_MAX / op2) return true; + if (op2 > UINT64_MAX / op1) return true; + return false; +} + +#else + +static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + return __builtin_uaddll_overflow((unsigned long long)op1, (unsigned long long)op2, + (unsigned long long *)result); +} + +static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + return __builtin_usubll_overflow((unsigned long long)op1, (unsigned long long)op2, + (unsigned long long *)result); +} + +bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + return __builtin_umulll_overflow((unsigned long long)op1, (unsigned long long)op2, + (unsigned long long *)result); +} + +#endif + +// @! Big int: Maybe look into those bool operations? +#pragma warning(push) +#pragma warning(disable : 4804) + +void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(dest != op1); + assert(dest != op2); + if (op1->digit_count == 0) { + return bigint_init_bigint(dest, op2); + } + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative == op2->is_negative) { + dest->is_negative = op1->is_negative; + + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + bool overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->digit); + if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + normalize(dest); + return; + } + unsigned i = 1; + uint64_t first_digit = dest->digit; + dest->digits = ALLOC_DIGITS(unsigned_max(op1->digit_count, op2->digit_count) + 1); + dest->digits[0] = first_digit; + + for (;;) { + bool found_digit = false; + uint64_t x = (uint64_t)overflow; + overflow = 0; + + if (i < op1->digit_count) { + found_digit = true; + uint64_t digit = op1_digits[i]; + overflow += add_u64_overflow(x, digit, &x); + } + + if (i < op2->digit_count) { + found_digit = true; + uint64_t digit = op2_digits[i]; + overflow += add_u64_overflow(x, digit, &x); + } + + dest->digits[i] = x; + i += 1; + + if (!found_digit) { + dest->digit_count = i; + normalize(dest); + return; + } + } + } + const BigInt *op_pos; + const BigInt *op_neg; + if (op1->is_negative) { + op_neg = op1; + op_pos = op2; + } + else { + op_pos = op1; + op_neg = op2; + } + + BigInt op_neg_abs = {0}; + bigint_negate(&op_neg_abs, op_neg); + const BigInt *bigger_op; + const BigInt *smaller_op; + switch (bigint_cmp(op_pos, &op_neg_abs)) { + case CMP_EQ: + bigint_init_unsigned(dest, 0); + return; + case CMP_LT: + bigger_op = &op_neg_abs; + smaller_op = op_pos; + dest->is_negative = true; + break; + case CMP_GT: + bigger_op = op_pos; + smaller_op = &op_neg_abs; + dest->is_negative = false; + break; + default: + FATAL_ERROR("UNREACHABLE"); + } + const uint64_t *bigger_op_digits = bigint_ptr(bigger_op); + const uint64_t *smaller_op_digits = bigint_ptr(smaller_op); + uint64_t overflow = (uint64_t)sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->digit); + if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) { + dest->digit_count = 1; + normalize(dest); + return; + } + uint64_t first_digit = dest->digit; + dest->digits = ALLOC_DIGITS(bigger_op->digit_count); + dest->digits[0] = first_digit; + unsigned i = 1; + + for (;;) { + bool found_digit = false; + uint64_t x = bigger_op_digits[i]; + uint64_t prev_overflow = overflow; + overflow = 0; + + if (i < smaller_op->digit_count) { + found_digit = true; + uint64_t digit = smaller_op_digits[i]; + overflow += sub_u64_overflow(x, digit, &x); + } + if (sub_u64_overflow(x, prev_overflow, &x)) { + found_digit = true; + overflow += 1; + } + dest->digits[i] = x; + i += 1; + + if (!found_digit || i >= bigger_op->digit_count) { + break; + } + } + assert(overflow == 0); + dest->digit_count = i; + normalize(dest); +} +#pragma warning(pop) + +void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_add(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) { + BigInt op2_negated = {0}; + bigint_negate(&op2_negated, op2); + return bigint_add(dest, op1, &op2_negated); +} + +void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt op2_negated = {0}; + bigint_negate(&op2_negated, op2); + return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed); +} + +static void mul_overflow(uint64_t op1, uint64_t op2, uint64_t *lo, uint64_t *hi) { + uint64_t u1 = (op1 & 0xffffffff); + uint64_t v1 = (op2 & 0xffffffff); + uint64_t t = (u1 * v1); + uint64_t w3 = (t & 0xffffffff); + uint64_t k = (t >> 32); + + op1 >>= 32; + t = (op1 * v1) + k; + k = (t & 0xffffffff); + uint64_t w1 = (t >> 32); + + op2 >>= 32; + t = (u1 * op2) + k; + k = (t >> 32); + + *hi = (op1 * op2) + w1 + k; + *lo = (t << 32) + w3; +} + +static void mul_scalar(BigInt *dest, const BigInt *op, uint64_t scalar) { + bigint_init_unsigned(dest, 0); + + BigInt bi_64; + bigint_init_unsigned(&bi_64, 64); + + const uint64_t *op_digits = bigint_ptr(op); + size_t i = op->digit_count - 1; + + while (1) { + BigInt shifted; + bigint_shl(&shifted, dest, &bi_64); + + uint64_t result_scalar; + uint64_t carry_scalar; + mul_overflow(scalar, op_digits[i], &result_scalar, &carry_scalar); + + BigInt result; + bigint_init_unsigned(&result, result_scalar); + + BigInt carry; + bigint_init_unsigned(&carry, carry_scalar); + + BigInt carry_shifted; + bigint_shl(&carry_shifted, &carry, &bi_64); + + BigInt tmp; + bigint_add(&tmp, &shifted, &carry_shifted); + + bigint_add(dest, &tmp, &result); + + if (i == 0) { + break; + } + i -= 1; + } +} + +void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(dest != op1); + assert(dest != op2); + if (op1->digit_count == 0 || op2->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + + uint64_t carry; + mul_overflow(op1_digits[0], op2_digits[0], &dest->digit, &carry); + if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) { + dest->is_negative = (op1->is_negative != op2->is_negative); + dest->digit_count = 1; + normalize(dest); + return; + } + + bigint_init_unsigned(dest, 0); + + BigInt bi_64; + bigint_init_unsigned(&bi_64, 64); + + size_t i = op2->digit_count - 1; + for (;;) { + BigInt shifted; + bigint_shl(&shifted, dest, &bi_64); + + BigInt scalar_result; + mul_scalar(&scalar_result, op1, op2_digits[i]); + + bigint_add(dest, &scalar_result, &shifted); + + if (i == 0) { + break; + } + i -= 1; + } + + dest->is_negative = (op1->is_negative != op2->is_negative); + normalize(dest); +} + +void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_mul(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +unsigned count_leading_zeros(uint32_t val) { + if (val == 0) return 32; + +#if defined(_MSC_VER) + unsigned long Index; + _BitScanReverse(&Index, val); + return Index ^ 31; +#else + return __builtin_clz(val); +#endif +} + +/// Make a 64-bit integer from a high / low pair of 32-bit integers. +static inline uint64_t make_64(uint32_t hi, uint32_t lo) { + return (((uint64_t)hi) << 32) | ((uint64_t)lo); +} + +/// Return the high 32 bits of a 64 bit value. +static inline uint32_t hi_32(uint64_t value) { + return (uint32_t)(value >> 32); +} + +/// Return the low 32 bits of a 64 bit value. +static inline uint32_t lo_32(uint64_t val) { + return (uint32_t)val; +} + +/// Implementation of Knuth's Algorithm D (Division of nonnegative integers) +/// from "Art of Computer Programming, Volume 2", section 4.3.1, p. 272. The +/// variables here have the same names as in the algorithm. Comments explain +/// the algorithm and any deviation from it. +static void knuth_div(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t *r, unsigned m, unsigned n) { + assert(u && "Must provide dividend"); + assert(v && "Must provide divisor"); + assert(q && "Must provide quotient"); + assert(u != v && u != q && v != q && "Must use different memory"); + assert(n > 1 && "n must be > 1"); + + // b denotes the base of the number system. In our case b is 2^32. + const uint64_t b = ((uint64_t)1) << 32; + + // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of + // u and v by d. Note that we have taken Knuth's advice here to use a power + // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of + // 2 allows us to shift instead of multiply and it is easy to determine the + // shift amount from the leading zeros. We are basically normalizing the u + // and v so that its high bits are shifted to the top of v's range without + // overflow. Note that this can require an extra word in u so that u must + // be of length m+n+1. + unsigned shift = count_leading_zeros(v[n - 1]); + uint32_t v_carry = 0; + uint32_t u_carry = 0; + if (shift) { + for (unsigned i = 0; i < m + n; ++i) { + uint32_t u_tmp = u[i] >> (32 - shift); + u[i] = (u[i] << shift) | u_carry; + u_carry = u_tmp; + } + for (unsigned i = 0; i < n; ++i) { + uint32_t v_tmp = v[i] >> (32 - shift); + v[i] = (v[i] << shift) | v_carry; + v_carry = v_tmp; + } + } + u[m + n] = u_carry; + + // D2. [Initialize j.] Set j to m. This is the loop counter over the places. + int j = (int)m; + do { + // D3. [Calculate q'.]. + // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q') + // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r') + // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease + // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test + // on v[n-2] determines at high speed most of the cases in which the trial + // value qp is one too large, and it eliminates all cases where qp is two + // too large. + uint64_t dividend = make_64(u[j + n], u[j + n - 1]); + uint64_t qp = dividend / v[n - 1]; + uint64_t rp = dividend % v[n - 1]; + if (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2]) { + qp--; + rp += v[n - 1]; + if (rp < b && (qp == b || qp * v[n - 2] > b * rp + u[j + n - 2])) { + qp--; + } + } + + // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with + // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation + // consists of a simple multiplication by a one-place number, combined with + // a subtraction. + // The digits (u[j+n]...u[j]) should be kept positive; if the result of + // this step is actually negative, (u[j+n]...u[j]) should be left as the + // true value plus b**(n+1), namely as the b's complement of + // the true value, and a "borrow" to the left should be remembered. + int64_t borrow = 0; + for (unsigned i = 0; i < n; ++i) { + uint64_t p = ((uint64_t)qp) * ((uint64_t)(v[i])); + int64_t subres = ((int64_t)(u[j + i])) - borrow - lo_32(p); + u[j + i] = lo_32((uint64_t)subres); + borrow = hi_32(p) - hi_32((uint64_t)subres); + } + bool is_neg = u[j + n] < borrow; + u[j + n] -= lo_32((uint64_t)borrow); + + // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was + // negative, go to step D6; otherwise go on to step D7. + q[j] = lo_32(qp); + if (is_neg) { + // D6. [Add back]. The probability that this step is necessary is very + // small, on the order of only 2/b. Make sure that test data accounts for + // this possibility. Decrease q[j] by 1 + q[j]--; + // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]). + // A carry will occur to the left of u[j+n], and it should be ignored + // since it cancels with the borrow that occurred in D4. + bool carry = false; + for (unsigned i = 0; i < n; i++) { + uint32_t limit = u32_min(u[j + i], v[i]); + u[j + i] += v[i] + carry; + carry = u[j + i] < limit || (carry && u[j + i] == limit); + } + u[j + n] += carry; + } + + // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3. + } while (--j >= 0); + + // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired + // remainder may be obtained by dividing u[...] by d. If r is non-null we + // compute the remainder (urem uses this). + if (r) { + // The value d is expressed by the "shift" value above since we avoided + // multiplication by d by using a shift left. So, all we have to do is + // shift right here. + if (shift) { + uint32_t carry = 0; + for (int i = (int)n - 1; i >= 0; i--) { + r[i] = (u[i] >> shift) | carry; + carry = u[i] << (32 - shift); + } + } + else { + for (int i = (int)n - 1; i >= 0; i--) { + r[i] = u[i]; + } + } + } +} + +static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) { + CmpRes cmp = bigint_cmp(op1, op2); + if (cmp == CMP_LT) { + if (!Quotient) { + bigint_init_unsigned(Quotient, 0); + } + if (!Remainder) { + bigint_init_bigint(Remainder, op1); + } + return; + } + if (cmp == CMP_EQ) { + if (!Quotient) { + bigint_init_unsigned(Quotient, 1); + } + if (!Remainder) { + bigint_init_unsigned(Remainder, 0); + } + return; + } + + const uint64_t *lhs = bigint_ptr(op1); + const uint64_t *rhs = bigint_ptr(op2); + unsigned lhsWords = op1->digit_count; + unsigned rhsWords = op2->digit_count; + + // First, compose the values into an array of 32-bit words instead of + // 64-bit words. This is a necessity of both the "short division" algorithm + // and the Knuth "classical algorithm" which requires there to be native + // operations for +, -, and * on an m bit value with an m*2 bit result. We + // can't use 64-bit operands here because we don't have native results of + // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't + // work on large-endian machines. + unsigned n = rhsWords * 2; + unsigned m = (lhsWords * 2) - n; + + // Allocate space for the temporary values we need either on the stack, if + // it will fit, or on the heap if it won't. + uint32_t space[128]; + uint32_t *U = NULL; + uint32_t *V = NULL; + uint32_t *Q = NULL; + uint32_t *R = NULL; + if ((Remainder ? 4 : 3) * n + 2 * m + 1 <= 128) { + U = &space[0]; + V = &space[m + n + 1]; + Q = &space[(m + n + 1) + n]; + if (Remainder) { + R = &space[(m + n + 1) + n + (m + n)]; + } + } + else { + U = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n + 1)); + V = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); + Q = (uint32_t *)malloc_arena(sizeof(uint32_t) * (m + n)); + if (Remainder) { + R = (uint32_t *)malloc_arena(sizeof(uint32_t) * n); + } + } + + // Initialize the dividend + memset(U, 0, (m + n + 1) * sizeof(uint32_t)); + for (unsigned i = 0; i < lhsWords; ++i) { + uint64_t tmp = lhs[i]; + U[i * 2] = lo_32(tmp); + U[i * 2 + 1] = hi_32(tmp); + } + U[m + n] = 0; // this extra word is for "spill" in the Knuth algorithm. + + // Initialize the divisor + memset(V, 0, (n) * sizeof(uint32_t)); + for (unsigned i = 0; i < rhsWords; ++i) { + uint64_t tmp = rhs[i]; + V[i * 2] = lo_32(tmp); + V[i * 2 + 1] = hi_32(tmp); + } + + // initialize the quotient and remainder + memset(Q, 0, (m + n) * sizeof(uint32_t)); + if (Remainder) memset(R, 0, n * sizeof(uint32_t)); + + // Now, adjust m and n for the Knuth division. n is the number of words in + // the divisor. m is the number of words by which the dividend exceeds the + // divisor (i.e. m+n is the length of the dividend). These sizes must not + // contain any zero words or the Knuth algorithm fails. + for (unsigned i = n; i > 0 && V[i - 1] == 0; i--) { + n--; + m++; + } + for (unsigned i = m + n; i > 0 && U[i - 1] == 0; i--) { + m--; + } + + // If we're left with only a single word for the divisor, Knuth doesn't work + // so we implement the short division algorithm here. This is much simpler + // and faster because we are certain that we can divide a 64-bit quantity + // by a 32-bit quantity at hardware speed and short division is simply a + // series of such operations. This is just like doing short division but we + // are using base 2^32 instead of base 10. + assert(n != 0 && "Divide by zero?"); + if (n == 1) { + uint32_t divisor = V[0]; + uint32_t rem = 0; + for (int i = (int)m; i >= 0; i--) { + uint64_t partial_dividend = make_64(rem, U[i]); + if (partial_dividend == 0) { + Q[i] = 0; + rem = 0; + } + else if (partial_dividend < divisor) { + Q[i] = 0; + rem = lo_32(partial_dividend); + } + else if (partial_dividend == divisor) { + Q[i] = 1; + rem = 0; + } + else { + Q[i] = lo_32(partial_dividend / divisor); + rem = lo_32(partial_dividend - (Q[i] * divisor)); + } + } + if (R) { + R[0] = rem; + } + } + else { + // Now we're ready to invoke the Knuth classical divide algorithm. In this + // case n > 1. + knuth_div(U, V, Q, R, m, n); + } + + // If the caller wants the quotient + if (Quotient) { + Quotient->is_negative = false; + Quotient->digit_count = lhsWords; + if (lhsWords == 1) { + Quotient->digit = make_64(Q[1], Q[0]); + } + else { + Quotient->digits = ALLOC_DIGITS(lhsWords); + for (size_t i = 0; i < lhsWords; i += 1) { + Quotient->digits[i] = make_64(Q[i * 2 + 1], Q[i * 2]); + } + } + } + + // If the caller wants the remainder + if (Remainder) { + Remainder->is_negative = false; + Remainder->digit_count = rhsWords; + if (rhsWords == 1) { + Remainder->digit = make_64(R[1], R[0]); + } + else { + Remainder->digits = ALLOC_DIGITS(rhsWords); + for (size_t i = 0; i < rhsWords; i += 1) { + Remainder->digits[i] = make_64(R[i * 2 + 1], R[i * 2]); + } + } + } +} + +void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(op2->digit_count != 0); // division by zero + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit = op1_digits[0] / op2_digits[0]; + dest->digit_count = 1; + dest->is_negative = op1->is_negative != op2->is_negative; + normalize(dest); + return; + } + if (op2->digit_count == 1 && op2_digits[0] == 1) { + // X / 1 == X + bigint_init_bigint(dest, op1); + dest->is_negative = op1->is_negative != op2->is_negative; + normalize(dest); + return; + } + + const BigInt *op1_positive; + BigInt op1_positive_data; + if (op1->is_negative) { + bigint_negate(&op1_positive_data, op1); + op1_positive = &op1_positive_data; + } + else { + op1_positive = op1; + } + + const BigInt *op2_positive; + BigInt op2_positive_data; + if (op2->is_negative) { + bigint_negate(&op2_positive_data, op2); + op2_positive = &op2_positive_data; + } + else { + op2_positive = op2; + } + + bigint_unsigned_division(op1_positive, op2_positive, dest, NULL); + dest->is_negative = op1->is_negative != op2->is_negative; + normalize(dest); +} + +void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->is_negative != op2->is_negative) { + bigint_div_trunc(dest, op1, op2); + BigInt mult_again = {0}; + bigint_mul(&mult_again, dest, op2); + mult_again.is_negative = op1->is_negative; + if (bigint_cmp(&mult_again, op1) != CMP_EQ) { + BigInt tmp = {0}; + bigint_init_bigint(&tmp, dest); + BigInt neg_one = {0}; + bigint_init_signed(&neg_one, -1); + bigint_add(dest, &tmp, &neg_one); + } + normalize(dest); + } + else { + bigint_div_trunc(dest, op1, op2); + } +} + +void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(op2->digit_count != 0); // division by zero + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit = op1_digits[0] % op2_digits[0]; + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + normalize(dest); + return; + } + if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) { + // special case this divisor + bigint_init_unsigned(dest, op1_digits[0]); + dest->is_negative = op1->is_negative; + normalize(dest); + return; + } + + if (op2->digit_count == 1 && op2_digits[0] == 1) { + // X % 1 == 0 + bigint_init_unsigned(dest, 0); + return; + } + + const BigInt *op1_positive; + BigInt op1_positive_data; + if (op1->is_negative) { + bigint_negate(&op1_positive_data, op1); + op1_positive = &op1_positive_data; + } + else { + op1_positive = op1; + } + + const BigInt *op2_positive; + BigInt op2_positive_data; + if (op2->is_negative) { + bigint_negate(&op2_positive_data, op2); + op2_positive = &op2_positive_data; + } + else { + op2_positive = op2; + } + + bigint_unsigned_division(op1_positive, op2_positive, NULL, dest); + dest->is_negative = op1->is_negative; + normalize(dest); +} + +void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->is_negative) { + BigInt first_rem; + bigint_rem(&first_rem, op1, op2); + first_rem.is_negative = !op2->is_negative; + BigInt op2_minus_rem; + bigint_add(&op2_minus_rem, op2, &first_rem); + bigint_rem(dest, &op2_minus_rem, op2); + dest->is_negative = false; + } + else { + bigint_rem(dest, op1, op2); + dest->is_negative = false; + } +} + +void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0) { + return bigint_init_bigint(dest, op2); + } + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative || op2->is_negative) { + size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = {0}; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = {0}; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = {0}; + bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } + else { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + dest->digit = op1_digits[0] | op2_digits[0]; + normalize(dest); + return; + } + dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + for (size_t i = 0; i < dest->digit_count; i += 1) { + uint64_t digit = 0; + if (i < op1->digit_count) { + digit |= op1_digits[i]; + } + if (i < op2->digit_count) { + digit |= op2_digits[i]; + } + dest->digits[i] = digit; + } + normalize(dest); + } +} + +void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0 || op2->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } + if (op1->is_negative || op2->is_negative) { + size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = {0}; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = {0}; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = {0}; + bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } + else { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + dest->digit = op1_digits[0] & op2_digits[0]; + normalize(dest); + return; + } + + dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + + size_t i = 0; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) { + dest->digits[i] = op1_digits[i] & op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + dest->digits[i] = 0; + } + normalize(dest); + } +} + +void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0) { + return bigint_init_bigint(dest, op2); + } + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative || op2->is_negative) { + size_t big_bit_count = size_max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = {0}; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = {0}; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = {0}; + bigint_xor(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } + else { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + + assert(op1->digit_count > 0 && op2->digit_count > 0); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + dest->digit = op1_digits[0] ^ op2_digits[0]; + normalize(dest); + return; + } + dest->digit_count = unsigned_max(op1->digit_count, op2->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + size_t i = 0; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) { + dest->digits[i] = op1_digits[i] ^ op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + if (i < op1->digit_count) { + dest->digits[i] = op1_digits[i]; + } + else if (i < op2->digit_count) { + dest->digits[i] = op2_digits[i]; + } + else { + FATAL_ERROR("Unreachable"); + } + } + normalize(dest); + } +} + +void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift); +void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(!op2->is_negative); + + if (op2->digit_count != 1) { + FATAL_ERROR("Unsupported: shift left by amount greater than 64 bit integer"); + } + bigint_shl_int(dest, op1, bigint_as_unsigned(op2)); +} + +void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift) { + if (shift == 0) { + bigint_init_bigint(dest, op1); + return; + } + + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + const uint64_t *op1_digits = bigint_ptr(op1); + + if (op1->digit_count == 1 && shift < 64) { + dest->digit = op1_digits[0] << shift; + if (dest->digit > op1_digits[0]) { + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + return; + } + } + + uint64_t digit_shift_count = shift / 64; + uint64_t leftover_shift_count = shift % 64; + + dest->digits = ALLOC_DIGITS(op1->digit_count + digit_shift_count + 1); + dest->digit_count = (unsigned)digit_shift_count; + uint64_t carry = 0; + for (size_t i = 0; i < op1->digit_count; i += 1) { + uint64_t digit = op1_digits[i]; + dest->digits[dest->digit_count] = carry | (digit << leftover_shift_count); + dest->digit_count++; + if (leftover_shift_count > 0) { + carry = digit >> (64 - leftover_shift_count); + } + else { + carry = 0; + } + } + dest->digits[dest->digit_count] = carry; + dest->digit_count += 1; + dest->is_negative = op1->is_negative; + normalize(dest); +} + +void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_shl(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(!op2->is_negative); + + if (op1->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } + + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + + if (op2->digit_count != 1) { + FATAL_ERROR("Unsupported: shift right by amount greater than 64 bit integer"); + } + + const uint64_t *op1_digits = bigint_ptr(op1); + uint64_t shift_amt = bigint_as_unsigned(op2); + + if (op1->digit_count == 1) { + dest->digit = shift_amt < 64 ? op1_digits[0] >> shift_amt : 0; + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + normalize(dest); + return; + } + + uint64_t digit_shift_count = shift_amt / 64; + uint64_t leftover_shift_count = shift_amt % 64; + + if (digit_shift_count >= op1->digit_count) { + return bigint_init_unsigned(dest, 0); + } + + dest->digit_count = (unsigned)(op1->digit_count - digit_shift_count); + uint64_t *digits; + if (dest->digit_count == 1) { + digits = &dest->digit; + } + else { + digits = ALLOC_DIGITS(dest->digit_count); + dest->digits = digits; + } + + uint64_t carry = 0; + for (size_t op_digit_index = op1->digit_count - 1;;) { + uint64_t digit = op1_digits[op_digit_index]; + size_t dest_digit_index = op_digit_index - digit_shift_count; + digits[dest_digit_index] = carry | (digit >> leftover_shift_count); + carry = digit << (64 - leftover_shift_count); + + if (dest_digit_index == 0) break; + op_digit_index -= 1; + } + dest->is_negative = op1->is_negative; + normalize(dest); +} + +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) { + BigInt zero; + bigint_init_unsigned(&zero, 0); + bigint_sub_wrap(dest, &zero, op, bit_count, true); +} + +void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { + if (bit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + if (is_signed) { + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, op, bit_count); + + BigInt inverted = {0}; + bigint_not(&inverted, &twos_comp, bit_count, false); + + from_twos_complement(dest, &inverted, bit_count, true); + return; + } + + assert(!op->is_negative); + + dest->is_negative = false; + const uint64_t *op_digits = bigint_ptr(op); + if (bit_count <= 64) { + dest->digit_count = 1; + if (op->digit_count == 0) { + if (bit_count == 64) { + dest->digit = UINT64_MAX; + } + else { + dest->digit = (1ULL << bit_count) - 1; + } + } + else if (op->digit_count == 1) { + dest->digit = ~op_digits[0]; + if (bit_count != 64) { + uint64_t + mask = (1ULL << bit_count) - 1; + dest->digit &= mask; + } + } + normalize(dest); + return; + } + dest->digit_count = (unsigned int)((bit_count + 63) / 64); + assert(dest->digit_count >= op->digit_count); + dest->digits = ALLOC_DIGITS(dest->digit_count); + size_t i = 0; + for (; i < op->digit_count; i += 1) { + dest->digits[i] = ~op_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + dest->digits[i] = 0xffffffffffffffffULL; + } + size_t digit_index = dest->digit_count - 1; + size_t digit_bit_index = bit_count % 64; + if (digit_bit_index != 0) { + uint64_t + mask = (1ULL << digit_bit_index) - 1; + dest->digits[digit_index] &= mask; + } + normalize(dest); +} + +void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed) { + BigInt twos_comp; + to_twos_complement(&twos_comp, op, bit_count); + from_twos_complement(dst, &twos_comp, bit_count, is_signed); +} + +CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2) { + if (op1->is_negative && !op2->is_negative) return CMP_LT; + if (!op1->is_negative && op2->is_negative) return CMP_GT; + if (op1->digit_count > op2->digit_count) return op1->is_negative ? CMP_LT : CMP_GT; + if (op2->digit_count > op1->digit_count) return op1->is_negative ? CMP_GT : CMP_LT; + if (op1->digit_count == 0) return CMP_EQ; + + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + for (unsigned i = op1->digit_count - 1;; i--) { + uint64_t op1_digit = op1_digits[i]; + uint64_t op2_digit = op2_digits[i]; + + if (op1_digit > op2_digit) { + return op1->is_negative ? CMP_LT : CMP_GT; + } + if (op1_digit < op2_digit) { + return op1->is_negative ? CMP_GT : CMP_LT; + } + if (i == 0) { + return CMP_EQ; + } + } +} + +void bigint_print(BigInt *bigint, uint64_t base) { + if (bigint->digit_count == 0) { + printf("0"); + return; + } + if (bigint->is_negative) { + printf("-"); + } + if (bigint->digit_count == 1 && base == 10) { + printf("%" PRIu64, bigint->digit); + return; + } + size_t len = bigint->digit_count * 64; + char *start = (char *)malloc_arena(len); + char *buf = start; + + BigInt digit_bi = {0}; + BigInt a1 = {0}; + BigInt a2 = {0}; + + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); + + BigInt base_bi = {0}; + bigint_init_unsigned(&base_bi, base); + + for (;;) { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) { + break; + } + } + + // reverse + + for (char *ptr = buf - 1; ptr >= start; ptr--) { + printf("%c", *ptr); + } +} + +const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, uint64_t base) { + Set_BigInt_Arena(allocator); + if (bigint->digit_count == 0) { + return "0"; + } + if (bigint->digit_count == 1 && base == 10) { + char *res = NULL; + if (bigint->is_negative) { + String string = string_fmt(allocator, "-%" PRIu64, bigint->digit); + return (const char *)string.str; + } + else { + String string = string_fmt(allocator, "%" PRIu64, bigint->digit); + return (const char *)string.str; + } + return res; + } + size_t len = bigint->digit_count * 64; + char *start = (char *)allocate_size(allocator, len); + char *buf = start; + + BigInt digit_bi = {0}; + BigInt a1 = {0}; + BigInt a2 = {0}; + + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); + + BigInt base_bi = {0}; + bigint_init_unsigned(&base_bi, base); + + for (;;) { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) { + break; + } + } + + // reverse + char *out = (char *)allocate_size(allocator, buf - start + 2); + char *current = out; + if (bigint->is_negative) { + *(current++) = '-'; + } + for (char *ptr = buf - 1; ptr >= start; ptr--) { + *(current++) = *ptr; + } + *(current++) = '\0'; + + return out; +} + +void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base) { + if (bigint->digit_count == 0) { + fprintf(file, "0"); + return; + } + if (bigint->is_negative) { + fprintf(file, "-"); + } + if (bigint->digit_count == 1 && base == 10) { + fprintf(file, "%" PRIu64, bigint->digit); + return; + } + size_t len = bigint->digit_count * 64; + char *start = (char *)malloc_arena(len); + char *buf = start; + + BigInt digit_bi = {0}; + BigInt a1 = {0}; + BigInt a2 = {0}; + + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, bigint); + + BigInt base_bi = {0}; + bigint_init_unsigned(&base_bi, base); + + for (;;) { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi); + *(buf++) = digit_to_char(digit, false); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CMP_EQ) { + break; + } + } + + // reverse + + for (char *ptr = buf - 1; ptr >= start; ptr--) { + fprintf(file, "%c", *ptr); + } +} +size_t bigint_popcount_unsigned(const BigInt *big_int) { + assert(!big_int->is_negative); + if (big_int->digit_count == 0) { + return 0; + } + + unsigned count = 0; + size_t bit_count = big_int->digit_count * 64; + for (size_t i = 0; i < bit_count; i++) { + if (bit_at_index(big_int, i)) { + count += 1; + } + } + return count; +} + +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) { + if (bit_count == 0) { + return 0; + } + if (bi->digit_count == 0) { + return 0; + } + + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, bi, bit_count); + + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(&twos_comp, i)) { + count += 1; + } + } + return count; +} + +size_t bigint_ctz(const BigInt *bi, size_t bit_count) { + if (bit_count == 0) { + return 0; + } + if (bi->digit_count == 0) { + return bit_count; + } + + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, bi, bit_count); + + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(&twos_comp, i)) { + return count; + } + count += 1; + } + return count; +} + +int64_t bigint_as_signed(const BigInt *bigint) { + if (bigint->digit_count == 0) return 0; + if (bigint->digit_count != 1) { + FATAL_ERROR("BigInt larger than i64"); + } + + if (bigint->is_negative) { + // TODO this code path is untested + if (bigint->digit <= 9223372036854775808ULL) { + return (-((int64_t)(bigint->digit - 1))) - 1; + } + FATAL_ERROR("BigInt does not fit in i64"); + } + return (int64_t)bigint->digit; +} + +CmpRes bigint_cmp_zero(const BigInt *op) { + if (op->digit_count == 0) { + return CMP_EQ; + } + return op->is_negative ? CMP_LT : CMP_GT; +} + +void bigint_incr(BigInt *x) { + if (!x->digit_count) { + bigint_init_unsigned(x, 1); + return; + } + + if (x->digit_count == 1) { + if (x->is_negative && x->digit != 0) { + x->digit -= 1; + return; + } + if (!x->is_negative && x->digit != UINT64_MAX) { + x->digit += 1; + return; + } + } + + BigInt copy; + bigint_init_bigint(©, x); + + BigInt one; + bigint_init_unsigned(&one, 1); + + bigint_add(x, ©, &one); +} + +double bigint_as_float(const BigInt *bigint) { + if (bigint_fits_in_bits(bigint, 64, bigint->is_negative)) { + return bigint->is_negative ? (double)bigint_as_signed(bigint) : (double)bigint_as_unsigned(bigint); + } + BigInt div; + uint64_t mult = 0x100000000000ULL; + double mul = 1; + bigint_init_unsigned(&div, mult); + BigInt current; + bigint_init_bigint(¤t, bigint); + double f = 0; + do { + BigInt temp; + bigint_mod(&temp, ¤t, &div); + f += bigint_as_signed(&temp) * mul; + mul *= mult; + bigint_div_trunc(&temp, ¤t, &div); + current = temp; + } while (current.digit_count > 0); + return f; +} diff --git a/c3_big_int.h b/src/language/c3_big_int.h similarity index 98% rename from c3_big_int.h rename to src/language/c3_big_int.h index ac141d6..277591c 100644 --- a/c3_big_int.h +++ b/src/language/c3_big_int.h @@ -1,56 +1,56 @@ -struct Token; - -#include -enum CmpRes { - CMP_LT, - CMP_GT, - CMP_EQ, -}; - -#define malloc_arena(x) allocate_size(bigint_allocator, x) -#define ALLOC_DIGITS(_digits) (uint64_t *)((_digits) ? malloc_arena(sizeof(uint64_t) * (_digits)) : NULL) -#define FATAL_ERROR(x) compiler_error(0, x) - -CORE_Static void compiler_error(Token *token, const char *str, ...); -const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, uint64_t base); -void bigint_init_unsigned(BigInt *big_int, uint64_t value); -void bigint_init_signed(BigInt *big_int, int64_t value); -void bigint_init_bigint(BigInt *dest, const BigInt *src); -void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative); -void bigint_negate(BigInt *dest, const BigInt *source); -size_t bigint_clz(const BigInt *big_int, size_t bit_count); -size_t bigint_ctz(const BigInt *big_int, size_t bit_count); -bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed); -void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian); -void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, bool is_signed); -void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift); -void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); -void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); -bool bigint_eql(BigInt a, BigInt b); -CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2); -CmpRes bigint_cmp_zero(const BigInt *op); -uint32_t bigint_hash(BigInt x); -void bigint_print(BigInt *bigint, uint64_t base); -void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base); -uint64_t bigint_as_unsigned(const BigInt *bigint); -int64_t bigint_as_signed(const BigInt *bigint); -double bigint_as_float(const BigInt *bigint); -void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed); -void bigint_incr(BigInt *x); -size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count); -size_t bigint_popcount_unsigned(const BigInt *big_int); +struct Token; + +#include +enum CmpRes { + CMP_LT, + CMP_GT, + CMP_EQ, +}; + +#define malloc_arena(x) allocate_size(bigint_allocator, x) +#define ALLOC_DIGITS(_digits) (uint64_t *)((_digits) ? malloc_arena(sizeof(uint64_t) * (_digits)) : NULL) +#define FATAL_ERROR(x) compiler_error(0, x) + +CORE_Static void compiler_error(Token *token, const char *str, ...); +const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, uint64_t base); +void bigint_init_unsigned(BigInt *big_int, uint64_t value); +void bigint_init_signed(BigInt *big_int, int64_t value); +void bigint_init_bigint(BigInt *dest, const BigInt *src); +void bigint_init_data(BigInt *dest, const uint64_t *digits, unsigned int digit_count, bool is_negative); +void bigint_negate(BigInt *dest, const BigInt *source); +size_t bigint_clz(const BigInt *big_int, size_t bit_count); +size_t bigint_ctz(const BigInt *big_int, size_t bit_count); +bool bigint_fits_in_bits(const BigInt *big_int, size_t bit_count, bool is_signed); +void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian); +void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, bool is_signed); +void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_shl_int(BigInt *dest, const BigInt *op1, uint64_t shift); +void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); +void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); +bool bigint_eql(BigInt a, BigInt b); +CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2); +CmpRes bigint_cmp_zero(const BigInt *op); +uint32_t bigint_hash(BigInt x); +void bigint_print(BigInt *bigint, uint64_t base); +void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base); +uint64_t bigint_as_unsigned(const BigInt *bigint); +int64_t bigint_as_signed(const BigInt *bigint); +double bigint_as_float(const BigInt *bigint); +void bigint_truncate(BigInt *dst, const BigInt *op, size_t bit_count, bool is_signed); +void bigint_incr(BigInt *x); +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count); +size_t bigint_popcount_unsigned(const BigInt *big_int); diff --git a/core_ast.cpp b/src/language/core_ast.cpp similarity index 96% rename from core_ast.cpp rename to src/language/core_ast.cpp index 94354a8..78d52c1 100644 --- a/core_ast.cpp +++ b/src/language/core_ast.cpp @@ -1,707 +1,707 @@ -void set_flag_typespec(Ast_Expr *expr); - -#define AST_NEW(T, ikind, ipos, iflags) \ - Ast_##T *result = allocate_struct(pctx->perm, Ast_##T); \ - result->flags = iflags; \ - result->kind = AST_##ikind; \ - result->parent_scope = pctx->currently_parsed_scope; \ - result->pos = ipos; \ - result->di = ++pctx->unique_ids - -#define ast_new(T, kind, pos, flags) (T *)_ast_new(sizeof(T), kind, pos, flags) -CORE_Static Ast * -_ast_new(size_t size, Ast_Kind kind, Token *pos, Ast_Flag flags = 0) { - Ast *result = (Ast *)allocate_size(pctx->perm, size); - result->flags = flags; - result->kind = kind; - result->parent_scope = pctx->currently_parsed_scope; - result->pos = pos; - result->di = ++pctx->unique_ids; - return result; -} - -CORE_Static Ast_Atom * -ast_str(Token *pos, Intern_String string) { - AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); - result->type = pctx->untyped_string; - result->intern_val = string; - return result; -} - -CORE_Static Ast_Atom * -ast_ident(Token *pos, Intern_String string) { - AST_NEW(Atom, IDENT, pos, AST_EXPR | AST_ATOM); - result->intern_val = string; - return result; -} - -CORE_Static Ast_Atom * -ast_bool(Token *pos, B32 bool_val) { - AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); - result->bool_val = bool_val; - result->type = pctx->untyped_bool; - return result; -} - -CORE_Static Ast_Atom * -ast_float(Token *pos, F64 value) { - AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); - result->type = pctx->untyped_float; - result->f64_val = value; - return result; -} - -CORE_Static Ast_Atom * -ast_int(Token *pos, BigInt val) { - AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); - result->type = pctx->untyped_int; - result->big_int_val = bigint_copy(pctx->perm, &val); - return result; -} - -CORE_Static Ast_Atom * -ast_int(Token *pos, U64 value) { - return ast_int(pos, bigint_u64(value)); -} - -CORE_Static Ast_Expr * -ast_expr_binary(Ast_Expr *left, Ast_Expr *right, Token *op) { - AST_NEW(Binary, BINARY, op, AST_EXPR); - result->op = op->kind; - result->left = left; - result->right = right; - return result; -} - -CORE_Static Ast_Call * -ast_call(Token *pos, Ast_Expr *name, Array exprs) { - // name here specifies also typespec for compound expressions ! - AST_NEW(Call, CALL, pos, AST_EXPR); - result->name = name; - result->exprs = exprs.tight_copy(pctx->perm); - return result; -} - -CORE_Static Ast_Call_Item * -ast_call_item(Token *pos, Ast_Atom *name, Ast_Expr *index, Ast_Expr *item) { - AST_NEW(Call_Item, CALL_ITEM, pos, AST_EXPR); - result->name = name; - result->item = item; - result->index = index; - return result; -} - -CORE_Static Ast_Expr * -ast_expr_unary(Token *pos, Token_Kind op, Ast_Expr *expr) { - AST_NEW(Unary, UNARY, pos, AST_EXPR); - result->flags = AST_EXPR; - result->expr = expr; - result->op = op; - return result; -} - -CORE_Static Ast_Expr * -ast_expr_index(Token *pos, Ast_Expr *expr, Ast_Expr *index) { - AST_NEW(Index, INDEX, pos, AST_EXPR); - result->flags = AST_EXPR; - result->expr = expr; - result->index = index; - return result; -} - -CORE_Static Ast_Lambda * -ast_lambda(Token *pos, Array params, Ast_Expr *ret, Ast_Scope *scope) { - AST_NEW(Lambda, LAMBDA_EXPR, pos, AST_EXPR); - result->flags = AST_EXPR; - result->args = params.tight_copy(pctx->perm); - result->ret = ret; - result->scope = scope; - if (scope) scope->parent_ast = result; - - For(params) { - if (is_flag_set(it->flags, AST_POLYMORPH)) { - set_flag(result->flags, AST_POLYMORPH); - break; - } - } - - return result; -} - -CORE_Static Ast_If * -ast_if(Token *pos, Array ifs) { - AST_NEW(If, IF, pos, AST_STMT); - result->ifs = ifs.tight_copy(pctx->perm); - return result; -} - -CORE_Static Ast_For * -ast_for(Token *pos, Ast_Expr *init, Ast_Expr *cond, Ast_Expr *iter, Ast_Scope *scope) { - AST_NEW(For, FOR, pos, AST_STMT); - result->init = init; - result->cond = cond; - result->iter = iter; - result->scope = scope; - scope->parent_ast = result; - return result; -} - -CORE_Static Ast_Pass * -ast_pass(Token *pos) { - AST_NEW(Pass, PASS, pos, AST_STMT); - return result; -} - -CORE_Static Ast_Break * -ast_break(Token *pos) { - AST_NEW(Break, BREAK, pos, AST_STMT); - return result; -} - -CORE_Static Ast_Break * -ast_compiler_breakpoint(Token *pos) { - AST_NEW(Break, COMPILER_BREAKPOINT_STMT, pos, AST_STMT); - return result; -} - -CORE_Static Ast_Pass * -ast_continue(Token *pos) { - AST_NEW(Pass, CONTINUE, pos, AST_STMT); - return result; -} - -CORE_Static Ast_Return * -ast_return(Token *pos, Ast_Expr *expr) { - AST_NEW(Return, RETURN, pos, AST_STMT); - result->expr = expr; - return result; -} - -CORE_Static Ast_Defer * -ast_defer(Token *pos, Ast_Scope *scope) { - Ast_Defer *result = ast_new(Ast_Defer, AST_DEFER, pos, AST_STMT); - result->scope = scope; - return result; -} - -CORE_Static Ast_Goto * -ast_goto(Token *pos, Intern_String label) { - Ast_Goto *result = ast_new(Ast_Goto, AST_GOTO, pos, AST_STMT); - result->label = label; - return result; -} - -CORE_Static Ast_Label * -ast_label(Token *pos, Intern_String name, Ast_Scope *scope, bool enable_goto) { - Ast_Label *result = ast_new(Ast_Label, AST_LABEL, pos, AST_STMT | AST_DECL); - result->enable_goto = enable_goto; - result->name = name; - result->scope = scope; - return result; -} - -CORE_Static Ast_If_Node * -ast_if_node(Token *pos, Ast_Expr *init, Ast_Expr *expr, Ast_Scope *scope) { - AST_NEW(If_Node, IF_NODE, pos, AST_STMT); - result->scope = scope; - result->expr = expr; - result->init = (Ast_Binary *)init; - scope->parent_ast = result; - if (result->init) { - assert(init->kind == AST_VAR); - } - return result; -} - -CORE_Static Ast_Array * -ast_array(Token *pos, Ast_Expr *expr) { - AST_NEW(Array, ARRAY, pos, AST_EXPR); - result->expr = expr; - return result; -} - -CORE_Static Ast_Scope * -begin_decl_scope(Allocator *scratch, Token *pos) { - AST_NEW(Scope, SCOPE, pos, AST_DECL); - result->file = pctx->currently_parsed_file; - result->module = pctx->currently_parsed_file->module; - result->scope_id = pctx->scope_ids++; - result->debug_name = pos->string; - assert(result->file); - pctx->currently_parsed_scope = result; - return result; -} - -CORE_Static void -finalize_decl_scope(Ast_Scope *scope) { - pctx->currently_parsed_scope = scope->parent_scope; -} - -CORE_Static Ast_Scope * -begin_stmt_scope(Allocator *scratch, Token *pos) { - AST_NEW(Scope, SCOPE, pos, AST_STMT); - result->stmts = {scratch}; - result->defers = {scratch}; - result->file = pctx->currently_parsed_file; - result->module = pctx->currently_parsed_file->module; - result->scope_id = pctx->scope_ids++; - result->debug_name = pos->string; - assert(result->file); - pctx->currently_parsed_scope = result; - return result; -} - -CORE_Static void -finalize_stmt_scope(Ast_Scope *scope) { - scope->stmts = scope->stmts.tight_copy(pctx->perm); - scope->defers = scope->defers.tight_copy(pctx->perm); - pctx->currently_parsed_scope = scope->parent_scope; -} - -CORE_Static Ast_Decl * -ast_struct(Token *pos, Ast_Scope *scope, Ast_Kind kind, Array polymorph_parameters) { - assert(kind == AST_STRUCT || kind == AST_UNION); - AST_NEW(Decl, STRUCT, pos, AST_DECL | AST_AGGREGATE); - result->kind = kind; - result->scope = scope; - scope->parent_decl = result; - if (polymorph_parameters.len) { - result->polymorph_parameters = polymorph_parameters; - set_flag(result->flags, AST_POLYMORPH); - set_flag(result->flags, AST_PARENT_POLYMORPH); - } - return result; -} - -CORE_Static Ast_Decl * -ast_enum(Token *pos, Ast_Expr *typespec, Ast_Scope *scope) { - AST_NEW(Decl, ENUM, pos, AST_DECL); - result->scope = scope; - result->typespec = typespec; - scope->parent_decl = result; - set_flag_typespec(typespec); - return result; -} - -CORE_Static Ast_Decl * -ast_var(Token *pos, Ast_Expr *typespec, Intern_String name, Ast_Expr *expr) { - AST_NEW(Decl, VAR, pos, AST_DECL); - result->name = name; - result->unique_name = name; - result->typespec = typespec; - set_flag_typespec(typespec); - result->expr = expr; - return result; -} - -CORE_Static Ast_Decl * -ast_const(Token *pos, Intern_String name, Value value) { - AST_NEW(Decl, CONST, pos, AST_DECL); - result->value = value; - result->name = name; - result->unique_name = name; - return result; -} - -CORE_Static Ast_Decl * -ast_const(Token *pos, Intern_String name, Ast_Expr *expr) { - AST_NEW(Decl, CONST, pos, AST_DECL); - result->expr = expr; - result->name = name; - result->unique_name = name; - return result; -} - -CORE_Static Ast_Decl * -ast_type(Token *pos, Intern_String name, Ast_Type *type) { - AST_NEW(Decl, TYPE, pos, AST_DECL); - result->type = pctx->type_type; - result->type_val = type; - result->name = name; - result->unique_name = name; - return result; -} - -CORE_Static Ast_Scope * -ast_decl_scope(Token *pos, Allocator *allocator, Ast_File *file) { - AST_NEW(Scope, SCOPE, pos, AST_DECL); - result->file = file; - - result->scope_id = pctx->scope_ids++; - assert(result->file); - return result; -} - -CORE_Static Ast_Decl * -ast_namespace(Token *pos, Ast_Scope *module, Intern_String name) { - AST_NEW(Decl, NAMESPACE, pos, AST_DECL); - result->scope = module; - result->name = name; - result->unique_name = name; - return result; -} - -CORE_Static Ast_Builtin * -ast_runtime_assert(Token *pos, Ast_Expr *expr, Intern_String message) { - AST_NEW(Builtin, RUNTIME_ASSERT, pos, AST_EXPR); - result->expr = expr; - result->assert_message = message; - return result; -} - -CORE_Static Ast_Builtin * -ast_constant_assert(Token *pos, Ast_Expr *expr, Intern_String message) { - AST_NEW(Builtin, CONSTANT_ASSERT, pos, AST_EXPR); - result->expr = expr; - result->assert_message = message; - return result; -} - -CORE_Static Ast_Builtin * -ast_sizeof(Token *pos, Ast_Expr *expr) { - AST_NEW(Builtin, SIZE_OF, pos, AST_EXPR); - result->expr = expr; - return result; -} - -CORE_Static Ast_Builtin * -ast_len(Token *pos, Ast_Expr *expr) { - AST_NEW(Builtin, LENGTH_OF, pos, AST_EXPR); - result->expr = expr; - return result; -} - -CORE_Static Ast_Builtin * -ast_alignof(Token *pos, Ast_Expr *expr) { - AST_NEW(Builtin, ALIGN_OF, pos, AST_EXPR); - result->expr = expr; - return result; -} - -CORE_Static Ast_Var_Unpack * -ast_var_unpack(Token *pos, Array vars, Ast_Expr *expr) { - AST_NEW(Var_Unpack, VAR_UNPACK, pos, AST_STMT); - result->vars = vars.tight_copy(pctx->perm); - result->expr = expr; - return result; -} - -// -// Value -// - -CORE_Static Value -value_bool(B32 v) { - Value value; - value.bool_val = v; - value.type = pctx->untyped_bool; - return value; -} - -CORE_Static Value -value_int(BigInt b) { - Value value; - value.big_int_val = b; - value.type = pctx->untyped_int; - return value; -} - -CORE_Static Value -value_int(S64 s64) { - Value value; - value.type = pctx->untyped_int; - bigint_init_signed(&value.big_int_val, s64); - return value; -} - -CORE_Static Value -value_float(F64 b) { - Value value; - value.f64_val = b; - value.type = pctx->untyped_float; - return value; -} - -CORE_Static Value -value_float(BigInt a) { - Value value; - value.f64_val = bigint_as_float(&a); - value.type = pctx->untyped_float; - return value; -} - -CORE_Static B32 -is_ident(Ast *ast) { - B32 result = ast->kind == AST_IDENT; - return result; -} - -CORE_Static B32 -is_binary(Ast *ast) { - B32 result = ast->kind == AST_BINARY; - return result; -} - -CORE_Static B32 -is_atom(Ast *ast) { - B32 result = is_flag_set(ast->flags, AST_ATOM); - return result; -} - -// -// Iterator -// - -const unsigned AST_NODE_END = 128; - -struct Ast_Iter { - Array stack; - Ast *ast; - Ast_Kind kind; - bool skip_end_blocks; - bool skip_children; - uint32_t visit_id; - uint32_t di; -}; - -uint32_t Ast_Iter_VisitIDGen; -Ast_Iter iterate_depth_first(Allocator *a, Ast *ast, bool skip_end_blocks = false) { - Ast_Iter result = {}; - result.stack = {a}; - result.ast = ast; - if (ast) result.kind = ast->kind; - result.visit_id = ++Ast_Iter_VisitIDGen; - result.skip_end_blocks = skip_end_blocks; - return result; -} - -void next(Ast_Iter *iter) { - Ast *ast = iter->ast; - ast->visit_id = iter->visit_id; - - if (iter->skip_children) { - iter->skip_children = false; - goto end_of_switch; - } - - switch (iter->kind) { - case AST_SCOPE: { - Ast_Scope *node = (Ast_Scope *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - For(node->stmts) iter->stack.add(it); - For(node->decls) iter->stack.add(it); - } break; - - case AST_MODULE: break; // This happens when we import stuff - case AST_FILE: invalid_codepath; break; - - case AST_IDENT: - case AST_VALUE: { - Ast_Atom *node = (Ast_Atom *)ast; - } break; - - case AST_INDEX: { - Ast_Index *node = (Ast_Index *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - iter->stack.add(node->index); - iter->stack.add(node->expr); - assert(node->index); - assert(node->expr); - } break; - - case AST_UNARY: { - Ast_Unary *node = (Ast_Unary *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - iter->stack.add(node->expr); - assert(node->expr); - } break; - - case AST_BINARY: { - Ast_Binary *node = (Ast_Binary *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - iter->stack.add(node->right); - iter->stack.add(node->left); - assert(node->right); - assert(node->left); - } break; - - case AST_CALL_ITEM: { - Ast_Call_Item *node = (Ast_Call_Item *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - iter->stack.add(node->item); - assert(node->item); - if (node->call_flags & CALL_INDEX) { - iter->stack.add(node->index); - assert(node->index); - } - else if (node->call_flags & CALL_NAME) { - iter->stack.add(node->name); - assert(node->name); - } - } break; - - case AST_COMPOUND: - case AST_CALL: { - Ast_Call *node = (Ast_Call *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - For(node->exprs) iter->stack.add(it); - if (node->name) iter->stack.add(node->name); - } break; - - case AST_TYPE_OF: - case AST_LENGTH_OF: - case AST_ALIGN_OF: - case AST_SIZE_OF: - case AST_RUNTIME_ASSERT: - case AST_CONSTANT_ASSERT: { - Ast_Builtin *node = (Ast_Builtin *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - iter->stack.add(node->expr); - assert(node->expr); - } break; - - case AST_SWITCH: { - Ast_Switch *node = (Ast_Switch *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - if (node->default_scope) iter->stack.add(node->default_scope); - For(node->cases) iter->stack.add(it); - iter->stack.add(node->value); - assert(node->value); - } break; - - case AST_SWITCH_CASE: { - Ast_Switch_Case *node = (Ast_Switch_Case *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - iter->stack.add(node->scope); - assert(node->scope); - For(node->labels) iter->stack.add(it); - } break; - - case AST_VAR_UNPACK: { - Ast_Var_Unpack *node = (Ast_Var_Unpack *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - iter->stack.add(node->expr); - assert(node->expr); - For(node->vars) iter->stack.add(it); - } break; - - case AST_DEFER: { - auto node = (Ast_Defer *)ast; - iter->stack.add(node->scope); - } break; - - case AST_BREAK: - case AST_PASS: - case AST_CONTINUE: - case AST_GOTO: - case AST_COMPILER_BREAKPOINT_STMT: { - } break; - - case AST_LABEL: - case AST_NAMESPACE: - case AST_STRUCT: - case AST_UNION: - case AST_ENUM: - case AST_LAMBDA: - case AST_TYPE: - case AST_CONST: - case AST_VAR: { - Ast_Decl *node = (Ast_Decl *)ast; - - if (!iter->skip_end_blocks) iter->stack.add(node); - if (node->scope) iter->stack.add(node->scope); - if (node->expr) iter->stack.add(node->expr); - if (node->typespec) iter->stack.add(node->typespec); - - // omitting polymorphs - // omitting polymorph parameters - } break; - - case AST_ARRAY: { - Ast_Array *node = (Ast_Array *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - if (node->expr) iter->stack.add(node->expr); - } break; - - case AST_FOR: { - Ast_For *node = (Ast_For *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - iter->stack.add(node->scope); - assert(node->scope); - if (node->iter) iter->stack.add(node->iter); - if (node->cond) iter->stack.add(node->cond); - if (node->init) iter->stack.add(node->init); - } break; - - case AST_IF: { - Ast_If *node = (Ast_If *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - For(node->ifs) iter->stack.add(it); - } break; - - case AST_IF_NODE: { - Ast_If_Node *node = (Ast_If_Node *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - iter->stack.add(node->scope); - assert(node->scope); - if (node->expr) iter->stack.add(node->expr); - if (node->init) iter->stack.add(node->init); - } break; - - case AST_RETURN: { - Ast_Return *node = (Ast_Return *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - if (node->expr) iter->stack.add(node->expr); - } break; - - case AST_LAMBDA_EXPR: { - Ast_Lambda *node = (Ast_Lambda *)ast; - if (!iter->skip_end_blocks) iter->stack.add(node); - if (node->scope) iter->stack.add(node->scope); - iter->stack.add(node->ret); - For(node->args) iter->stack.add(it); - } break; - - default: assert(!"Invalid default case"); - } -end_of_switch: - - if (iter->stack.len <= 0) { - iter->ast = 0; - iter->kind = AST_NONE; - iter->stack.dealloc(); - return; - } - - iter->ast = iter->stack.pop(); - assert(iter->ast != 0); - iter->di += 1; - iter->kind = iter->ast->kind; - if (iter->ast->visit_id == iter->visit_id) { - iter->kind = (Ast_Kind)((unsigned)iter->kind + AST_NODE_END); - } -} - -CORE_Static void -set_flag_typespec(Ast_Expr *expr) { - for (Ast_Iter iter = iterate_depth_first(pctx->heap, expr, true); iter.ast; next(&iter)) { - set_flag(iter.ast->flags, AST_TYPESPEC); - } -} - -CORE_Static void -unset_polymorph(Ast *ast) { - for (Ast_Iter iter = iterate_depth_first(pctx->heap, ast, true); iter.ast; next(&iter)) { - unset_flag(iter.ast->flags, AST_POLYMORPH); - } -} - -CORE_Static bool -is_typespec_polymorphic(Ast *ast) { - for (Ast_Iter iter = iterate_depth_first(pctx->heap, ast, true); iter.ast; next(&iter)) { - if (is_flag_set(iter.ast->flags, AST_POLYMORPH)) { - return true; - } - } - return false; -} +void set_flag_typespec(Ast_Expr *expr); + +#define AST_NEW(T, ikind, ipos, iflags) \ + Ast_##T *result = allocate_struct(pctx->perm, Ast_##T); \ + result->flags = iflags; \ + result->kind = AST_##ikind; \ + result->parent_scope = pctx->currently_parsed_scope; \ + result->pos = ipos; \ + result->di = ++pctx->unique_ids + +#define ast_new(T, kind, pos, flags) (T *)_ast_new(sizeof(T), kind, pos, flags) +CORE_Static Ast * +_ast_new(size_t size, Ast_Kind kind, Token *pos, Ast_Flag flags = 0) { + Ast *result = (Ast *)allocate_size(pctx->perm, size); + result->flags = flags; + result->kind = kind; + result->parent_scope = pctx->currently_parsed_scope; + result->pos = pos; + result->di = ++pctx->unique_ids; + return result; +} + +CORE_Static Ast_Atom * +ast_str(Token *pos, Intern_String string) { + AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); + result->type = pctx->untyped_string; + result->intern_val = string; + return result; +} + +CORE_Static Ast_Atom * +ast_ident(Token *pos, Intern_String string) { + AST_NEW(Atom, IDENT, pos, AST_EXPR | AST_ATOM); + result->intern_val = string; + return result; +} + +CORE_Static Ast_Atom * +ast_bool(Token *pos, B32 bool_val) { + AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); + result->bool_val = bool_val; + result->type = pctx->untyped_bool; + return result; +} + +CORE_Static Ast_Atom * +ast_float(Token *pos, F64 value) { + AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); + result->type = pctx->untyped_float; + result->f64_val = value; + return result; +} + +CORE_Static Ast_Atom * +ast_int(Token *pos, BigInt val) { + AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); + result->type = pctx->untyped_int; + result->big_int_val = bigint_copy(pctx->perm, &val); + return result; +} + +CORE_Static Ast_Atom * +ast_int(Token *pos, U64 value) { + return ast_int(pos, bigint_u64(value)); +} + +CORE_Static Ast_Expr * +ast_expr_binary(Ast_Expr *left, Ast_Expr *right, Token *op) { + AST_NEW(Binary, BINARY, op, AST_EXPR); + result->op = op->kind; + result->left = left; + result->right = right; + return result; +} + +CORE_Static Ast_Call * +ast_call(Token *pos, Ast_Expr *name, Array exprs) { + // name here specifies also typespec for compound expressions ! + AST_NEW(Call, CALL, pos, AST_EXPR); + result->name = name; + result->exprs = exprs.tight_copy(pctx->perm); + return result; +} + +CORE_Static Ast_Call_Item * +ast_call_item(Token *pos, Ast_Atom *name, Ast_Expr *index, Ast_Expr *item) { + AST_NEW(Call_Item, CALL_ITEM, pos, AST_EXPR); + result->name = name; + result->item = item; + result->index = index; + return result; +} + +CORE_Static Ast_Expr * +ast_expr_unary(Token *pos, Token_Kind op, Ast_Expr *expr) { + AST_NEW(Unary, UNARY, pos, AST_EXPR); + result->flags = AST_EXPR; + result->expr = expr; + result->op = op; + return result; +} + +CORE_Static Ast_Expr * +ast_expr_index(Token *pos, Ast_Expr *expr, Ast_Expr *index) { + AST_NEW(Index, INDEX, pos, AST_EXPR); + result->flags = AST_EXPR; + result->expr = expr; + result->index = index; + return result; +} + +CORE_Static Ast_Lambda * +ast_lambda(Token *pos, Array params, Ast_Expr *ret, Ast_Scope *scope) { + AST_NEW(Lambda, LAMBDA_EXPR, pos, AST_EXPR); + result->flags = AST_EXPR; + result->args = params.tight_copy(pctx->perm); + result->ret = ret; + result->scope = scope; + if (scope) scope->parent_ast = result; + + For(params) { + if (is_flag_set(it->flags, AST_POLYMORPH)) { + set_flag(result->flags, AST_POLYMORPH); + break; + } + } + + return result; +} + +CORE_Static Ast_If * +ast_if(Token *pos, Array ifs) { + AST_NEW(If, IF, pos, AST_STMT); + result->ifs = ifs.tight_copy(pctx->perm); + return result; +} + +CORE_Static Ast_For * +ast_for(Token *pos, Ast_Expr *init, Ast_Expr *cond, Ast_Expr *iter, Ast_Scope *scope) { + AST_NEW(For, FOR, pos, AST_STMT); + result->init = init; + result->cond = cond; + result->iter = iter; + result->scope = scope; + scope->parent_ast = result; + return result; +} + +CORE_Static Ast_Pass * +ast_pass(Token *pos) { + AST_NEW(Pass, PASS, pos, AST_STMT); + return result; +} + +CORE_Static Ast_Break * +ast_break(Token *pos) { + AST_NEW(Break, BREAK, pos, AST_STMT); + return result; +} + +CORE_Static Ast_Break * +ast_compiler_breakpoint(Token *pos) { + AST_NEW(Break, COMPILER_BREAKPOINT_STMT, pos, AST_STMT); + return result; +} + +CORE_Static Ast_Pass * +ast_continue(Token *pos) { + AST_NEW(Pass, CONTINUE, pos, AST_STMT); + return result; +} + +CORE_Static Ast_Return * +ast_return(Token *pos, Ast_Expr *expr) { + AST_NEW(Return, RETURN, pos, AST_STMT); + result->expr = expr; + return result; +} + +CORE_Static Ast_Defer * +ast_defer(Token *pos, Ast_Scope *scope) { + Ast_Defer *result = ast_new(Ast_Defer, AST_DEFER, pos, AST_STMT); + result->scope = scope; + return result; +} + +CORE_Static Ast_Goto * +ast_goto(Token *pos, Intern_String label) { + Ast_Goto *result = ast_new(Ast_Goto, AST_GOTO, pos, AST_STMT); + result->label = label; + return result; +} + +CORE_Static Ast_Label * +ast_label(Token *pos, Intern_String name, Ast_Scope *scope, bool enable_goto) { + Ast_Label *result = ast_new(Ast_Label, AST_LABEL, pos, AST_STMT | AST_DECL); + result->enable_goto = enable_goto; + result->name = name; + result->scope = scope; + return result; +} + +CORE_Static Ast_If_Node * +ast_if_node(Token *pos, Ast_Expr *init, Ast_Expr *expr, Ast_Scope *scope) { + AST_NEW(If_Node, IF_NODE, pos, AST_STMT); + result->scope = scope; + result->expr = expr; + result->init = (Ast_Binary *)init; + scope->parent_ast = result; + if (result->init) { + assert(init->kind == AST_VAR); + } + return result; +} + +CORE_Static Ast_Array * +ast_array(Token *pos, Ast_Expr *expr) { + AST_NEW(Array, ARRAY, pos, AST_EXPR); + result->expr = expr; + return result; +} + +CORE_Static Ast_Scope * +begin_decl_scope(Allocator *scratch, Token *pos) { + AST_NEW(Scope, SCOPE, pos, AST_DECL); + result->file = pctx->currently_parsed_file; + result->module = pctx->currently_parsed_file->module; + result->scope_id = pctx->scope_ids++; + result->debug_name = pos->string; + assert(result->file); + pctx->currently_parsed_scope = result; + return result; +} + +CORE_Static void +finalize_decl_scope(Ast_Scope *scope) { + pctx->currently_parsed_scope = scope->parent_scope; +} + +CORE_Static Ast_Scope * +begin_stmt_scope(Allocator *scratch, Token *pos) { + AST_NEW(Scope, SCOPE, pos, AST_STMT); + result->stmts = {scratch}; + result->defers = {scratch}; + result->file = pctx->currently_parsed_file; + result->module = pctx->currently_parsed_file->module; + result->scope_id = pctx->scope_ids++; + result->debug_name = pos->string; + assert(result->file); + pctx->currently_parsed_scope = result; + return result; +} + +CORE_Static void +finalize_stmt_scope(Ast_Scope *scope) { + scope->stmts = scope->stmts.tight_copy(pctx->perm); + scope->defers = scope->defers.tight_copy(pctx->perm); + pctx->currently_parsed_scope = scope->parent_scope; +} + +CORE_Static Ast_Decl * +ast_struct(Token *pos, Ast_Scope *scope, Ast_Kind kind, Array polymorph_parameters) { + assert(kind == AST_STRUCT || kind == AST_UNION); + AST_NEW(Decl, STRUCT, pos, AST_DECL | AST_AGGREGATE); + result->kind = kind; + result->scope = scope; + scope->parent_decl = result; + if (polymorph_parameters.len) { + result->polymorph_parameters = polymorph_parameters; + set_flag(result->flags, AST_POLYMORPH); + set_flag(result->flags, AST_PARENT_POLYMORPH); + } + return result; +} + +CORE_Static Ast_Decl * +ast_enum(Token *pos, Ast_Expr *typespec, Ast_Scope *scope) { + AST_NEW(Decl, ENUM, pos, AST_DECL); + result->scope = scope; + result->typespec = typespec; + scope->parent_decl = result; + set_flag_typespec(typespec); + return result; +} + +CORE_Static Ast_Decl * +ast_var(Token *pos, Ast_Expr *typespec, Intern_String name, Ast_Expr *expr) { + AST_NEW(Decl, VAR, pos, AST_DECL); + result->name = name; + result->unique_name = name; + result->typespec = typespec; + set_flag_typespec(typespec); + result->expr = expr; + return result; +} + +CORE_Static Ast_Decl * +ast_const(Token *pos, Intern_String name, Value value) { + AST_NEW(Decl, CONST, pos, AST_DECL); + result->value = value; + result->name = name; + result->unique_name = name; + return result; +} + +CORE_Static Ast_Decl * +ast_const(Token *pos, Intern_String name, Ast_Expr *expr) { + AST_NEW(Decl, CONST, pos, AST_DECL); + result->expr = expr; + result->name = name; + result->unique_name = name; + return result; +} + +CORE_Static Ast_Decl * +ast_type(Token *pos, Intern_String name, Ast_Type *type) { + AST_NEW(Decl, TYPE, pos, AST_DECL); + result->type = pctx->type_type; + result->type_val = type; + result->name = name; + result->unique_name = name; + return result; +} + +CORE_Static Ast_Scope * +ast_decl_scope(Token *pos, Allocator *allocator, Ast_File *file) { + AST_NEW(Scope, SCOPE, pos, AST_DECL); + result->file = file; + + result->scope_id = pctx->scope_ids++; + assert(result->file); + return result; +} + +CORE_Static Ast_Decl * +ast_namespace(Token *pos, Ast_Scope *module, Intern_String name) { + AST_NEW(Decl, NAMESPACE, pos, AST_DECL); + result->scope = module; + result->name = name; + result->unique_name = name; + return result; +} + +CORE_Static Ast_Builtin * +ast_runtime_assert(Token *pos, Ast_Expr *expr, Intern_String message) { + AST_NEW(Builtin, RUNTIME_ASSERT, pos, AST_EXPR); + result->expr = expr; + result->assert_message = message; + return result; +} + +CORE_Static Ast_Builtin * +ast_constant_assert(Token *pos, Ast_Expr *expr, Intern_String message) { + AST_NEW(Builtin, CONSTANT_ASSERT, pos, AST_EXPR); + result->expr = expr; + result->assert_message = message; + return result; +} + +CORE_Static Ast_Builtin * +ast_sizeof(Token *pos, Ast_Expr *expr) { + AST_NEW(Builtin, SIZE_OF, pos, AST_EXPR); + result->expr = expr; + return result; +} + +CORE_Static Ast_Builtin * +ast_len(Token *pos, Ast_Expr *expr) { + AST_NEW(Builtin, LENGTH_OF, pos, AST_EXPR); + result->expr = expr; + return result; +} + +CORE_Static Ast_Builtin * +ast_alignof(Token *pos, Ast_Expr *expr) { + AST_NEW(Builtin, ALIGN_OF, pos, AST_EXPR); + result->expr = expr; + return result; +} + +CORE_Static Ast_Var_Unpack * +ast_var_unpack(Token *pos, Array vars, Ast_Expr *expr) { + AST_NEW(Var_Unpack, VAR_UNPACK, pos, AST_STMT); + result->vars = vars.tight_copy(pctx->perm); + result->expr = expr; + return result; +} + +// +// Value +// + +CORE_Static Value +value_bool(B32 v) { + Value value; + value.bool_val = v; + value.type = pctx->untyped_bool; + return value; +} + +CORE_Static Value +value_int(BigInt b) { + Value value; + value.big_int_val = b; + value.type = pctx->untyped_int; + return value; +} + +CORE_Static Value +value_int(S64 s64) { + Value value; + value.type = pctx->untyped_int; + bigint_init_signed(&value.big_int_val, s64); + return value; +} + +CORE_Static Value +value_float(F64 b) { + Value value; + value.f64_val = b; + value.type = pctx->untyped_float; + return value; +} + +CORE_Static Value +value_float(BigInt a) { + Value value; + value.f64_val = bigint_as_float(&a); + value.type = pctx->untyped_float; + return value; +} + +CORE_Static B32 +is_ident(Ast *ast) { + B32 result = ast->kind == AST_IDENT; + return result; +} + +CORE_Static B32 +is_binary(Ast *ast) { + B32 result = ast->kind == AST_BINARY; + return result; +} + +CORE_Static B32 +is_atom(Ast *ast) { + B32 result = is_flag_set(ast->flags, AST_ATOM); + return result; +} + +// +// Iterator +// + +const unsigned AST_NODE_END = 128; + +struct Ast_Iter { + Array stack; + Ast *ast; + Ast_Kind kind; + bool skip_end_blocks; + bool skip_children; + uint32_t visit_id; + uint32_t di; +}; + +uint32_t Ast_Iter_VisitIDGen; +Ast_Iter iterate_depth_first(Allocator *a, Ast *ast, bool skip_end_blocks = false) { + Ast_Iter result = {}; + result.stack = {a}; + result.ast = ast; + if (ast) result.kind = ast->kind; + result.visit_id = ++Ast_Iter_VisitIDGen; + result.skip_end_blocks = skip_end_blocks; + return result; +} + +void next(Ast_Iter *iter) { + Ast *ast = iter->ast; + ast->visit_id = iter->visit_id; + + if (iter->skip_children) { + iter->skip_children = false; + goto end_of_switch; + } + + switch (iter->kind) { + case AST_SCOPE: { + Ast_Scope *node = (Ast_Scope *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + For(node->stmts) iter->stack.add(it); + For(node->decls) iter->stack.add(it); + } break; + + case AST_MODULE: break; // This happens when we import stuff + case AST_FILE: invalid_codepath; break; + + case AST_IDENT: + case AST_VALUE: { + Ast_Atom *node = (Ast_Atom *)ast; + } break; + + case AST_INDEX: { + Ast_Index *node = (Ast_Index *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + iter->stack.add(node->index); + iter->stack.add(node->expr); + assert(node->index); + assert(node->expr); + } break; + + case AST_UNARY: { + Ast_Unary *node = (Ast_Unary *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + iter->stack.add(node->expr); + assert(node->expr); + } break; + + case AST_BINARY: { + Ast_Binary *node = (Ast_Binary *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + iter->stack.add(node->right); + iter->stack.add(node->left); + assert(node->right); + assert(node->left); + } break; + + case AST_CALL_ITEM: { + Ast_Call_Item *node = (Ast_Call_Item *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + iter->stack.add(node->item); + assert(node->item); + if (node->call_flags & CALL_INDEX) { + iter->stack.add(node->index); + assert(node->index); + } + else if (node->call_flags & CALL_NAME) { + iter->stack.add(node->name); + assert(node->name); + } + } break; + + case AST_COMPOUND: + case AST_CALL: { + Ast_Call *node = (Ast_Call *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + For(node->exprs) iter->stack.add(it); + if (node->name) iter->stack.add(node->name); + } break; + + case AST_TYPE_OF: + case AST_LENGTH_OF: + case AST_ALIGN_OF: + case AST_SIZE_OF: + case AST_RUNTIME_ASSERT: + case AST_CONSTANT_ASSERT: { + Ast_Builtin *node = (Ast_Builtin *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + iter->stack.add(node->expr); + assert(node->expr); + } break; + + case AST_SWITCH: { + Ast_Switch *node = (Ast_Switch *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + if (node->default_scope) iter->stack.add(node->default_scope); + For(node->cases) iter->stack.add(it); + iter->stack.add(node->value); + assert(node->value); + } break; + + case AST_SWITCH_CASE: { + Ast_Switch_Case *node = (Ast_Switch_Case *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + iter->stack.add(node->scope); + assert(node->scope); + For(node->labels) iter->stack.add(it); + } break; + + case AST_VAR_UNPACK: { + Ast_Var_Unpack *node = (Ast_Var_Unpack *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + iter->stack.add(node->expr); + assert(node->expr); + For(node->vars) iter->stack.add(it); + } break; + + case AST_DEFER: { + auto node = (Ast_Defer *)ast; + iter->stack.add(node->scope); + } break; + + case AST_BREAK: + case AST_PASS: + case AST_CONTINUE: + case AST_GOTO: + case AST_COMPILER_BREAKPOINT_STMT: { + } break; + + case AST_LABEL: + case AST_NAMESPACE: + case AST_STRUCT: + case AST_UNION: + case AST_ENUM: + case AST_LAMBDA: + case AST_TYPE: + case AST_CONST: + case AST_VAR: { + Ast_Decl *node = (Ast_Decl *)ast; + + if (!iter->skip_end_blocks) iter->stack.add(node); + if (node->scope) iter->stack.add(node->scope); + if (node->expr) iter->stack.add(node->expr); + if (node->typespec) iter->stack.add(node->typespec); + + // omitting polymorphs + // omitting polymorph parameters + } break; + + case AST_ARRAY: { + Ast_Array *node = (Ast_Array *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + if (node->expr) iter->stack.add(node->expr); + } break; + + case AST_FOR: { + Ast_For *node = (Ast_For *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + iter->stack.add(node->scope); + assert(node->scope); + if (node->iter) iter->stack.add(node->iter); + if (node->cond) iter->stack.add(node->cond); + if (node->init) iter->stack.add(node->init); + } break; + + case AST_IF: { + Ast_If *node = (Ast_If *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + For(node->ifs) iter->stack.add(it); + } break; + + case AST_IF_NODE: { + Ast_If_Node *node = (Ast_If_Node *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + iter->stack.add(node->scope); + assert(node->scope); + if (node->expr) iter->stack.add(node->expr); + if (node->init) iter->stack.add(node->init); + } break; + + case AST_RETURN: { + Ast_Return *node = (Ast_Return *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + if (node->expr) iter->stack.add(node->expr); + } break; + + case AST_LAMBDA_EXPR: { + Ast_Lambda *node = (Ast_Lambda *)ast; + if (!iter->skip_end_blocks) iter->stack.add(node); + if (node->scope) iter->stack.add(node->scope); + iter->stack.add(node->ret); + For(node->args) iter->stack.add(it); + } break; + + default: assert(!"Invalid default case"); + } +end_of_switch: + + if (iter->stack.len <= 0) { + iter->ast = 0; + iter->kind = AST_NONE; + iter->stack.dealloc(); + return; + } + + iter->ast = iter->stack.pop(); + assert(iter->ast != 0); + iter->di += 1; + iter->kind = iter->ast->kind; + if (iter->ast->visit_id == iter->visit_id) { + iter->kind = (Ast_Kind)((unsigned)iter->kind + AST_NODE_END); + } +} + +CORE_Static void +set_flag_typespec(Ast_Expr *expr) { + for (Ast_Iter iter = iterate_depth_first(pctx->heap, expr, true); iter.ast; next(&iter)) { + set_flag(iter.ast->flags, AST_TYPESPEC); + } +} + +CORE_Static void +unset_polymorph(Ast *ast) { + for (Ast_Iter iter = iterate_depth_first(pctx->heap, ast, true); iter.ast; next(&iter)) { + unset_flag(iter.ast->flags, AST_POLYMORPH); + } +} + +CORE_Static bool +is_typespec_polymorphic(Ast *ast) { + for (Ast_Iter iter = iterate_depth_first(pctx->heap, ast, true); iter.ast; next(&iter)) { + if (is_flag_set(iter.ast->flags, AST_POLYMORPH)) { + return true; + } + } + return false; +} diff --git a/core_codegen_c_language.cpp b/src/language/core_codegen_c_language.cpp similarity index 96% rename from core_codegen_c_language.cpp rename to src/language/core_codegen_c_language.cpp index e7a67fe..133e663 100644 --- a/core_codegen_c_language.cpp +++ b/src/language/core_codegen_c_language.cpp @@ -1,1071 +1,1071 @@ -// @todo @cleanup -// :PrinterCleanup -// Instead of relying on global state, this probably should -// use a String_Builder or some kind of Builder context + macros -#define gen(...) pctx->gen.addf(__VA_ARGS__) -#define genln(...) \ - do { \ - gen("\n"); \ - gen_indent(); \ - gen(__VA_ARGS__); \ - } while (0) -global S32 global_indent; -global S32 is_inside_struct; -/*@language_refactor: Add context to typechecking and codegen, use arrays to construct stacks - instead of passing arguments to functions you use the context stacks -*/ - -CORE_Static void gen_ast(Ast *ast); -CORE_Static bool gen_expr(Ast_Expr *ast); -CORE_Static void gen_stmt_scope(Ast_Scope *scope, B32 switch_case_gen_break = 0); - -CORE_Static void -gen_indent() { - for (S32 i = 0; i < global_indent; i++) gen(" "); -} - -global Intern_String last_filename; -global int last_line; -CORE_Static void -gen_line(Ast *node) { - if (pctx->emit_line_directives) { - last_line = node->pos->line + 1; - genln("#line %d", last_line); - if (node->pos->file != last_filename) { - last_filename = node->pos->file; - gen(" \"%Q\"", last_filename); - } - } -} - -CORE_Static void -gen_last_line() { - if (pctx->emit_line_directives) { - genln("#line %d", last_line); - } -} - -CORE_Static const char * -get_ctype_name_for_type(Ast_Type *type) { - switch (type->kind) { - case TYPE_VOID: return "void"; - case TYPE_BOOL: return "bool"; - case TYPE_STRING: return "String"; - case TYPE_F32: return "float"; - case TYPE_F64: return "double"; - - case TYPE_CHAR: return "char"; - case TYPE_UCHAR: return "unsigned char"; - case TYPE_INT: return "int"; - case TYPE_UINT: return "unsigned int"; - case TYPE_LONG: return "long"; - case TYPE_ULONG: return "unsigned long"; - case TYPE_LLONG: return "long long"; - case TYPE_ULLONG: return "unsigned long long"; - case TYPE_SHORT: return "short"; - case TYPE_USHORT: return "unsigned short"; - - case TYPE_S8: return "int8_t"; - case TYPE_S16: return "int16_t"; - case TYPE_S32: return "int32_t"; - case TYPE_S64: return "int64_t"; - case TYPE_U8: return "uint8_t"; - case TYPE_U16: return "uint16_t"; - case TYPE_U32: return "uint32_t"; - case TYPE_U64: return "uint64_t"; - case TYPE_TYPE: return "int64_t"; - - case TYPE_INCOMPLETE: { - // This happens in case where we have a procedure in an away module. - // Meaning that it's not resolved fully on purpose (treeshaking). - // And that procedure takes a pointer from a struct that is never - // used elsewhere. In that case this seems fine. - return "void"; - } - invalid_default_case; - } - return ""; -} - -CORE_Static String -string_simple_decl_prefix(Ast_Type *ast) { - switch (ast->kind) { - case TYPE_POINTER: { - String string = string_simple_decl_prefix(ast->base); - string = pctx->fmt("%Q*", string); - return string; - } break; - case TYPE_LAMBDA: return {}; break; - case TYPE_ENUM: - case TYPE_ARRAY: { - return string_simple_decl_prefix(ast->base); - } break; - case TYPE_SLICE: { - String string = string_simple_decl_prefix(ast->base); - string = pctx->fmt("Slice%llu ", ast->type_id); - return string; - } break; - case TYPE_UNION: - case TYPE_STRUCT: { - auto constant = (Ast_Decl *)ast->ast; - auto name = constant->unique_name; - String string = pctx->fmt("%Q ", name); - String sc = {}; - return pctx->fmt("%Q%Q", sc, string); - } break; - default: { - String string = pctx->fmt("%s ", get_ctype_name_for_type(ast)); - return string; - } - } - return {}; -} - -CORE_Static String -string_simple_decl_postfix(Ast_Type *ast) { - switch (ast->kind) { - case TYPE_POINTER: - return string_simple_decl_postfix(ast->base); - break; - case TYPE_ARRAY: { - String result = string_simple_decl_postfix(ast->arr.base); - String string = pctx->fmt("[%d]%Q", ast->arr.size, result); - return string; - } break; - case TYPE_LAMBDA: break; - case TYPE_SLICE: - case TYPE_ENUM: - case TYPE_STRUCT: break; - default: - break; - // default: return string_from_cstring((char *)name(ast)); - } - return {}; -} - -CORE_Static String -string_simple_decl(Ast_Type *ast, Intern_String name = {}) { - if (ast->kind == TYPE_LAMBDA) { - String prefix = string_simple_decl_prefix(ast->func.ret); - String string = pctx->fmt("%Q(*%Q)(", prefix, name); - For(ast->func.args) { - String prefix_arg = string_simple_decl_prefix(it); - string = pctx->fmt("%Q%Q", string, prefix_arg); - if (&it != ast->func.args.end() - 1) - string = pctx->fmt("%Q, ", string); - } - string = pctx->fmt("%Q)", string); - return string; - } - else { - String string = string_simple_decl_prefix(ast); - if (name.len) { - string = pctx->fmt("%Q%Q", string, name); - } - String postfix = string_simple_decl_postfix(ast); - string = pctx->fmt("%Q%Q", string, postfix); - return string; - } -} - -CORE_Static void -gen_simple_decl(Ast_Type *ast, Intern_String name = {}) { - String string = string_simple_decl(ast, name); - gen("%Q", string); -} - -CORE_Static String -get_type_postfix(Ast_Type *type) { - switch (type->kind) { - case TYPE_F32: return "f"_s; break; - case TYPE_ULLONG: - case TYPE_U64: return "ULL"_s; break; - case TYPE_LLONG: - case TYPE_S64: return "LL"_s; break; - case TYPE_U8: - case TYPE_UCHAR: - case TYPE_U16: - case TYPE_UINT: - case TYPE_U32: return "U"_s; break; - - case TYPE_SHORT: - case TYPE_LONG: - case TYPE_F64: - case TYPE_S8: - case TYPE_S16: - case TYPE_S32: - case TYPE_INT: - case TYPE_CHAR: - return ""_s; - break; - invalid_default_case; - } - assert(!"Unhandled case or error"); - return ""_s; -} - -CORE_Static B32 -gen_value(Token *pos, Value a) { - if (is_untyped(a.type)) compiler_error(pos, "Internal compiler error: Untyped got propagated to the codegen stage"); - - B32 result = true; - Ast_Type *type = a.type; - if (is_enum(a.type)) { - type = a.type->base; - } - - switch (type->kind) { - CASE_INT : { - Scoped_Arena scratch(pctx->scratch); - String postfix = get_type_postfix(type); - const char *string = bigint_to_error_string(scratch.arena, &a.big_int_val, 10); - gen("%s%Q", string, postfix); - } break; - case TYPE_POINTER: { - if (a.type == pctx->type_pointer_to_char) { - gen("\"%Q\"", a.intern_val); - } - else { - U64 pointer_value = bigint_as_unsigned(&a.big_int_val); - gen("0x%llx", pointer_value); - } - } break; - CASE_BOOL : { - a.bool_val ? gen("true") : gen("false"); - } break; - CASE_FLOAT : { - String postfix = get_type_postfix(type); - gen("%f%Q", a.f64_val, postfix); - } break; - case TYPE_TYPE: { - gen("%d", a.type_val->type_id); - } break; - default: { - if (is_string(type)) { - int length = 0; - gen("CORE_ConditionalCompound(String){(uint8_t *)\""); - for (int i = 0; i < a.intern_val.len; i++) { - if (a.intern_val.str[i] == '\n') { - length += 2; - gen("\\n"); - } - else if (a.intern_val.str[i] == '\r') { - length += 2; - gen("\\r"); - } - else { - length += 1; - gen("%c", a.intern_val.str[i]); - } - } - gen("\", %d}", length); - } - else result = false; - } - } - return result; -} - -CORE_Static void -gen_defers_for_scope(Ast_Scope *scope) { - int save_line = last_line; - For(scope->defers.reverse()) { - gen("/* defer */"); - gen_stmt_scope(it->scope); - } - last_line = save_line; -} - -CORE_Static void -gen_defers_for_scope_and_parents(Ast_Scope *scope) { - for (; scope;) { - gen_defers_for_scope(scope); - scope = scope->parent_scope; - } -} - -CORE_Static void -gen_stmt_scope(Ast_Scope *scope, B32 switch_case_gen_break) { - gen("{"); - global_indent++; - For(scope->stmts) { - gen_line(it); - genln(""); - gen_ast(it); - } - gen_defers_for_scope(scope); - if (switch_case_gen_break == 1) genln("break;"); - global_indent--; - gen_last_line(); - genln("}"); -} - -enum { - ALWAYS_EMIT_VALUE = 0, - DONT_EMIT_VALUE = 1, -}; - -CORE_Static void -gen_pointer(Ast_Expr *expr) { - gen("&"); - if (is_flag_set(expr->flags, AST_IS_LVALUE)) { - gen_expr(expr); - } - else { - gen("("); - gen_simple_decl(expr->resolved_type); - gen(")"); - gen("{"); - gen_expr(expr); - gen("}"); - } -} - -CORE_Static void gen_try_any_or_slice(Ast_Expr *expr, Ast_Type *decl_type); -CORE_Static void -gen_compound(Ast *ast, bool generate_compound_decl = true) { - assert(ast->kind == AST_COMPOUND); - auto node = (Ast_Call *)ast; - - bool generated_anything = false; - bool is_global_variable = node->parent_scope->kind == AST_FILE; - if (!is_global_variable && generate_compound_decl) { - gen("("); - gen_simple_decl(node->resolved_type); - gen(")"); - } - gen("{"); - - // We need to double wrap it because Any is a pointer - // We need to pass it a compound array - if (is_slice(node->resolved_type)) { - generated_anything = true; - gen(".len = %d, ", node->exprs.len); - gen(".data = ("); - gen_simple_decl(node->resolved_type->base); - gen("[]"); - gen(")"); - gen("{"); - } - - For(node->exprs) { - generated_anything = true; - if (is_struct(node->resolved_type) || is_union(node->resolved_type)) - gen(".%Q = ", it->resolved_name); - else if (is_array(node->resolved_type)) - gen("[%d] = ", (int)it->resolved_index); - gen_try_any_or_slice(it->item, it->resolved_type); - - if (!node->exprs.is_last(it)) gen(", "); - } - - if (is_slice(node->resolved_type)) gen("}"); - if (!generated_anything) gen("0"); - gen("}"); -} - -CORE_Static void -gen_try_any_or_slice(Ast_Expr *expr, Ast_Type *decl_type) { - // We want normal values to get boxed as Any pointers - // but other then that we shouldn't box other Any values - if (expr->kind != AST_COMPOUND && is_any(decl_type) && !is_any(expr->resolved_type)) { - gen("(Any){"); - gen_pointer(expr); - gen(", %d}", expr->resolved_type->type_id); - } - else if (expr->kind == AST_IDENT && is_slice(decl_type)) { - Ast_Atom *atom = (Ast_Atom *)expr; - gen("("); - gen_simple_decl(decl_type); - gen(")"); - gen("{%d, ", atom->resolved_type->arr.size); - gen_expr(expr); - gen("}"); - } - else gen_expr(expr); -} - -CORE_Static void -gen_var(Ast_Decl *decl, B32 emit_value, B32 scope_names) { - if (is_flag_set(decl->flags, AST_FOREIGN)) gen("extern "); - gen_simple_decl(decl->type, decl->unique_name); - if (is_flag_set(decl->flags, AST_FOREIGN)) return; - - if (emit_value == DONT_EMIT_VALUE) { - return; - } - - if (decl->expr) { - if (is_array(decl->type)) { - gen_compound((Ast_Call *)decl->expr, false); - } - else { - gen(" = "); - gen_try_any_or_slice(decl->expr, decl->type); - } - } - else { // Default zero - if (is_numeric(decl->type)) { - gen(" = 0"); - } - else { - gen(" = {0}"); - } - } -} - -CORE_Static void -gen_lambda(Intern_String name, Ast_Lambda *lambda, B32 generate_block = true) { - gen_simple_decl(lambda->resolved_type->func.ret, name); - gen("("); - if (lambda->args.len == 0) gen("void"); - For(lambda->args) { - if (it->name == pctx->intern("..."_s)) { - gen("..."); - continue; - } - gen_var(it, DONT_EMIT_VALUE, true); - if (&it != (lambda->args.end() - 1)) - gen(", "); - } - gen(")"); - - if (generate_block && lambda->scope) { - gen_stmt_scope(lambda->scope); - } - else gen(";"); -} - -CORE_Static bool -gen_expr(Ast_Expr *ast) { - switch (ast->kind) { - CASE(IDENT, Atom) { - if (node->resolved_decl->kind == AST_NAMESPACE) - return false; - - bool is_global_variable = is_flag_set(node->resolved_decl->flags, AST_GLOBAL); - if (is_global_variable) { - gen("%Q", node->resolved_decl->unique_name); - } - - else { - gen("%Q", node->intern_val); - } - - BREAK(); - } - - CASE(VALUE, Atom) { - B32 written = gen_value(node->pos, node->value); - if (!written) { - gen("%Q", node->value.intern_val); - } - BREAK(); - } - - CASE(ARRAY, Array) { - gen("%d", node->resolved_type->type_id); - BREAK(); - } - - CASE(INDEX, Index) { - gen("("); - gen_expr(node->expr); - if (node->index_original_type == pctx->type_string || node->index_original_type == pctx->untyped_string) { - gen(".data"); - } - else if (is_slice(node->index_original_type)) { - gen(".data"); - } - gen("["); - gen_expr(node->index); - gen("]"); - gen(")"); - BREAK(); - } - - CASE(LAMBDA_EXPR, Lambda) { - gen_lambda({}, node); - BREAK(); - } - - CASE(BINARY, Binary) { - if (node->op == TK_Dot) { - if (gen_expr(node->left)) { - if (node->dot_access_step_resolution && node->dot_access_step_resolution->kind == TYPE_POINTER) gen("->"); - else gen("."); - } - gen_expr(node->right); - return true; - } - - else if (node->op == TK_Arrow) { - gen("("); - gen("("); - gen_simple_decl(node->resolved_type); - gen(")"); - gen_expr(node->left); - gen(")"); - return true; - } - - else if (node->resolved_operator_overload) { - gen("%Q(", node->resolved_operator_overload->unique_name); - gen_expr(node->left); - gen(", "); - gen_expr(node->right); - gen(")"); - } - - else { - if (!token_is_assign(node->op)) gen("("); - gen_expr(node->left); - gen("%s", name(node->op)); - gen_expr(node->right); - if (!token_is_assign(node->op)) gen(")"); - } - - BREAK(); - } - - CASE(UNARY, Unary) { - if (node->resolved_operator_overload) { - gen("%Q(", node->resolved_operator_overload->unique_name); - gen_expr(node->expr); - gen(")"); - } - - else { - gen("("); - if (node->op != TK_PostIncrement && node->op != TK_PostDecrement) gen("%s", name(node->op)); - gen_expr(node->expr); - if (node->op == TK_PostIncrement || node->op == TK_PostDecrement) gen("%s", name(node->op)); - gen(")"); - } - BREAK(); - } - - CASE(VAR, Decl) { - gen_ast(node); - BREAK(); - } - - CASE(LENGTH_OF, Call) { - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - gen_expr(expr); - if (is_pointer(expr->resolved_type)) - gen("->len"); - else gen(".len"); - BREAK(); - } - - CASE(CALL, Call) { - gen("%Q(", node->resolved_decl->unique_name); - For(node->exprs) { - gen_try_any_or_slice(it->item, it->resolved_type); - if (!node->exprs.is_last(it)) gen(", "); - } - gen(")"); - BREAK(); - } - - CASE(COMPOUND, Call) { - gen_compound(node); - BREAK(); - } - - invalid_default_case; - } - return true; -} - -/* -- [ ] Assigning arrays to arrays doesn't work in C. Being able to do that seems like a good idea though - -*/ - -CORE_Static void -gen_ast(Ast *ast) { - switch (ast->kind) { - - CASE(RUNTIME_ASSERT, Builtin) { - if (node->assert_message.len == 0) gen("Assert"); - else gen("AssertMessage"); - gen("("); - gen_expr(node->expr); - if (node->assert_message.len) { - gen(", \"%Q\"", node->assert_message); - } - gen(");"); - BREAK(); - } - - CASE(RETURN, Return) { - gen_defers_for_scope_and_parents(node->parent_scope); - gen("return "); - if (node->expr) gen_expr(node->expr); - gen(";"); - BREAK(); - } - - CASE(VAR, Decl) { - gen_var(node, is_inside_struct ? DONT_EMIT_VALUE : ALWAYS_EMIT_VALUE, true); - if (!is_flag_set(ast->flags, AST_EXPR)) gen(";"); - BREAK(); - } - - CASE(IF, If) { - For(node->ifs) { - gen_line(it); - genln(""); - if (it->init) { - gen_expr(it->init); - gen(";"); - genln(""); - } - if (node->ifs.is_first(it)) { - gen("if("); - gen_expr(it->expr); - gen(")"); - gen_stmt_scope(it->scope); - } - else { - gen("else"); - if (it->expr) { - gen(" if("); - gen_expr(it->expr); - gen(")"); - } - gen_stmt_scope(it->scope); - } - } - BREAK(); - } - - case AST_GOTO: { - auto n = (Ast_Goto *)ast; - gen("goto %Q;", n->label); - } break; - - case AST_LABEL: { - auto n = (Ast_Label *)ast; - if (n->enable_goto == false) gen("/* %Q */", n->name); - if (n->enable_goto == true) gen("%Q:", n->name); - gen_stmt_scope(n->scope); - } break; - - case AST_CONTINUE: { - gen_defers_for_scope(ast->parent_scope); - gen("continue;"); - } break; - - case AST_BREAK: { - gen_defers_for_scope(ast->parent_scope); - gen("break;"); - } break; - - case AST_DEFER: { - gen("// defer"); - } break; - - case AST_COMPILER_BREAKPOINT_STMT: { - __debugbreak(); - } break; - - case AST_PASS: { - gen("// pass"); - } break; - - CASE(BINARY, Binary) { - gen_expr(node); - gen(";"); - BREAK(); - } - - CASE(FOR, For) { - // Array iter - if (node->is_array_traversal) { - gen("for(int64_t _i%d = 0; _i%d < ", node->pos->line, node->pos->line); - if (is_array(node->cond->resolved_type)) { - gen("BufferSize("); - gen_expr(node->cond); - gen(")"); - } - else { - assert(is_slice(node->cond->resolved_type)); - gen_expr(node->cond); - gen(".len"); - } - - gen("; _i%d+=1)", node->pos->line); - gen("{"); - global_indent++; - genln(""); - gen_simple_decl(node->array_traversal_var->type, node->array_traversal_var->name); - gen(" = "); - gen_expr(node->cond); - if (node->is_also_slice_traversal) gen(".data"); - gen(" + _i%d;", node->pos->line); - For(node->scope->stmts) { - gen_line(it); - genln(""); - gen_ast(it); - } - global_indent--; - gen_last_line(); - gen_defers_for_scope(ast->parent_scope); - genln("}"); - } - - // Normal for loop - else { - gen("for("); - if (node->init) gen_expr(node->init); - gen(";"); - if (node->cond) gen_expr(node->cond); - gen(";"); - if (node->iter) gen_expr(node->iter); - gen(")"); - gen_stmt_scope(node->scope); - } - - BREAK(); - } - - CASE(LAMBDA, Decl) { - gen_line(node); - genln(""); - - if (is_flag_set(node->expr->flags, AST_FOREIGN)) { - gen("/*foreign*/"); - } - gen_lambda(node->unique_name, node->lambda); - BREAK(); - } - - case AST_UNION: - CASE(STRUCT, Decl) { - gen("%s ", node->kind == AST_STRUCT ? "struct" : "union"); - gen("%Q{", node->unique_name); - global_indent++; - is_inside_struct++; - For(node->scope->decls) { - genln(""); - gen_ast(it); - } - - is_inside_struct--; - global_indent--; - genln("};"); - BREAK(); - } - - CASE(ENUM, Decl) { - gen("/*enum %Q{", node->unique_name); - // @todo add typespec - global_indent++; - For(node->scope->decls) { - genln("%Q", it->unique_name); - gen(" = "); - gen_value(it->pos, it->value); - gen(","); - } - global_indent--; - genln("};*/"); - BREAK(); - } - - case AST_TYPE: - CASE(CONST, Decl) { - unused(node); - BREAK(); - } - - CASE(SWITCH, Switch) { - gen("switch("); - gen_expr(node->value); - gen("){"); - global_indent++; - - For(node->cases) { - For2(it->labels, label) { - gen_line(it); - genln(""); - gen("case "); - gen_expr(label); - gen(":"); - } - - gen_stmt_scope(it->scope, it->fallthrough ? false : true); - } - - if (node->default_scope) { - genln("default: "); - gen_stmt_scope(node->default_scope); - } - - global_indent--; - gen_last_line(); - genln("}"); - - BREAK(); - } - - CASE(VAR_UNPACK, Var_Unpack) { - Ast_Type *t = node->resolved_type; - For(node->vars) { - gen_ast(it); - } - - Intern_String var_name = get_unique_name(); - gen_simple_decl(node->resolved_type, var_name); - gen(" = "); - gen_expr(node->expr); - gen(";"); - - int i = 0; - For(node->vars) { - Ast_Resolved_Member &m = t->agg.members[i]; - char *p = "&"; - if (is_array(m.type)) p = ""; - - gen("MemoryCopy((void *)%s%Q, (void *)%s%Q.%Q, sizeof(%Q));", p, it->name, p, var_name, m.name, it->name); - i = +1; - } - BREAK(); - } - - case AST_CONSTANT_ASSERT: - case AST_NAMESPACE: break; - - default: { - assert(is_flag_set(ast->flags, AST_EXPR)); - gen_expr((Ast_Expr *)ast); - gen(";"); - } - } -} - -CORE_Static String -compile_to_c_code() { - pctx->time.code_generation = os_time(); - - if (pctx->single_header_library_mode) { - gen(R"( -/* - Do this: - #define %Q_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define %Q_IMPLEMENTATION - #include "%Q.h" - - You can #define Assert(x) to avoid using default assert - You can #define AssertMessage(x) to get more comprehensive error info - You can #define MemoryCopy(x) to avoid using default memory copy -*/ - )", - pctx->single_header_library_name, pctx->single_header_library_name, pctx->single_header_library_name); - genln("#ifndef %Q_LIBRARY_HEADER ", pctx->single_header_library_name); - genln("#define %Q_LIBRARY_HEADER ", pctx->single_header_library_name); - } - - gen(R"( - -#include -#include -#include - -int printf(const char *format, ...); -#ifndef Assert - #define Assert(x) do{ \ - if(!(x)) { \ - printf("Assertion failed! %%s(%%d): %%s\n", __FILE__, __LINE__, #x); \ - *(volatile int *)0 = 0; \ - } \ - }while(0) -#endif - -#ifndef AssertMessage - #define AssertMessage(x,...) Assert(x) -#endif - -#ifndef MemoryCopy - #define MemoryCopy MemoryCopy_ - - static void - MemoryCopy_(void *dst, void *src, size_t size){ - uint8_t *d = (uint8_t*)dst; - uint8_t *s = (uint8_t*)src; - for(size_t i = 0; i < size; i++){ - d[i] = s[i]; - } - } -#endif - -#define BufferSize(x) ((int)(sizeof(x)/sizeof((x)[0]))) - -#ifdef __clang__ - #define CORE_ConditionalCompound(x) (x) -#elif _MSC_VER - #define CORE_ConditionalCompound(x) -#else - #define CORE_ConditionalCompound(x) (x) -#endif - -)"); - - // Generate struct forward decls - For(pctx->ordered_decls) { - if (it->kind == AST_STRUCT) { - genln("typedef struct %Q %Q;", it->unique_name, it->unique_name); - } - else if (it->kind == AST_UNION) { - genln("typedef union %Q %Q;", it->unique_name, it->unique_name); - } - } - - // Generate slice types - For2(pctx->all_types, type) { - if (type->kind == TYPE_SLICE) { - genln("typedef struct Slice%llu{", type->type_id); - global_indent++; - genln("int64_t len;"); - genln(""); - gen_simple_decl(type_pointer(type->base), pctx->intern("data"_s)); - gen(";"); - global_indent--; - genln("} Slice%llu;", type->type_id); - } - } - - Intern_String intern_main = pctx->intern("main"_s); - Intern_String intern_win_main = pctx->intern("WinMain"_s); - - Ast_Decl *main = 0; - Ast_Decl *win_main = 0; - - // Generate lambda forward decls - For(pctx->ordered_decls) { - if (it->kind == AST_LAMBDA) { - if (it->name == intern_main) { - main = it; - it->unique_name = it->name; - } - - if (it->name == intern_win_main) { - win_main = it; - it->unique_name = it->name; - } - - genln(""); - - gen_lambda(it->unique_name, it->lambda, false); - } - } - - if (!main && !win_main) { - compiler_error(0, "Entry point is not defined! Try main or WinMain"); - } - - if (pctx->emit_type_info) { - // Generate language.core - for (S32 i = 0; i < pctx->base_language_ordered_decl_len; i++) { - Ast_Decl *it = get(&pctx->ordered_decls, i); - genln(""); - gen_ast(it); - } - - // Generate type info - genln("int type_infos_len = %d;", length(&pctx->all_types)); - genln("Type_Info *type_infos = (Type_Info[]){"); - global_indent++; - int i = 0; - For2(pctx->all_types, t) { - if (t->kind == TYPE_POLYMORPH) continue; - if (i++ != t->type_id) { - compiler_error(0, "Internal compiler error: type info array is inconsistent"); - } - - genln("{/*%Q*/", typestring(t)); - global_indent += 1; - genln(".kind = %d, ", (S32)t->kind); - genln(".size = %d, ", (S32)t->size); - genln(".align = %d, ", (S32)t->align); - genln(".is_unsigned = %s, ", t->is_unsigned ? "true" : "false"); - genln(".type = %d, ", t->type_id); - - switch (t->kind) { - case TYPE_POINTER: - case TYPE_SLICE: { - genln(".base_type = %d", t->base->type_id); - } break; - case TYPE_ARRAY: { - genln(".base_type = %d, ", t->base->type_id); - genln(".array_size = %d", t->arr.size); - } break; - case TYPE_LAMBDA: { - if (t->func.args.len == 0) goto end_of_switch; - genln(".lambda_return = %d, ", t->func.ret->type_id); - genln(".lambda_arguments.len = %d, ", t->func.args.len); - genln(".lambda_arguments.data = (Type_Info[%d]){", t->func.args.len); - global_indent += 1; - For2(t->func.args, arg) { - genln("{.type = %d}, ", arg->type_id); - } - global_indent -= 1; - genln("}"); - } break; - case TYPE_UNION: - case TYPE_STRUCT: { - if (t->agg.members.len == 0) goto end_of_switch; - genln(".struct_members.len = %d, ", t->agg.members.len); - genln(".struct_members.data = (Type_Info_Struct_Member[%d]){", t->agg.members.len); - global_indent += 1; - For2(t->agg.members, m) { - genln("{.name = {(uint8_t *)\"%Q\", %d}, .type = %d, .offset = %d}, ", m.name, m.name.len, m.type->type_id, m.offset); - } - global_indent -= 1; - genln("}"); - } break; - case AST_ENUM: { - // todo; - } break; - default: { - } - } - end_of_switch:; - global_indent -= 1; - genln("},"); - } - global_indent--; - genln("};"); - } - - if (pctx->single_header_library_mode) { - genln("#endif"); - genln("#ifdef %Q_IMPLEMENTATION ", pctx->single_header_library_name); - } - - // Generate actual code - int index = 0; - For(pctx->ordered_decls) { - if (index >= pctx->base_language_ordered_decl_len) { - genln(""); - gen_ast(it); - } - index += 1; - } - - if (pctx->single_header_library_mode) { - genln("#endif"); - } - - String string_result = string_flatten(pctx->perm, &pctx->gen); - pctx->time.code_generation = os_time() - pctx->time.code_generation; - return string_result; -} +// @todo @cleanup +// :PrinterCleanup +// Instead of relying on global state, this probably should +// use a String_Builder or some kind of Builder context + macros +#define gen(...) pctx->gen.addf(__VA_ARGS__) +#define genln(...) \ + do { \ + gen("\n"); \ + gen_indent(); \ + gen(__VA_ARGS__); \ + } while (0) +global S32 global_indent; +global S32 is_inside_struct; +/*@language_refactor: Add context to typechecking and codegen, use arrays to construct stacks + instead of passing arguments to functions you use the context stacks +*/ + +CORE_Static void gen_ast(Ast *ast); +CORE_Static bool gen_expr(Ast_Expr *ast); +CORE_Static void gen_stmt_scope(Ast_Scope *scope, B32 switch_case_gen_break = 0); + +CORE_Static void +gen_indent() { + for (S32 i = 0; i < global_indent; i++) gen(" "); +} + +global Intern_String last_filename; +global int last_line; +CORE_Static void +gen_line(Ast *node) { + if (pctx->emit_line_directives) { + last_line = node->pos->line + 1; + genln("#line %d", last_line); + if (node->pos->file != last_filename) { + last_filename = node->pos->file; + gen(" \"%Q\"", last_filename); + } + } +} + +CORE_Static void +gen_last_line() { + if (pctx->emit_line_directives) { + genln("#line %d", last_line); + } +} + +CORE_Static const char * +get_ctype_name_for_type(Ast_Type *type) { + switch (type->kind) { + case TYPE_VOID: return "void"; + case TYPE_BOOL: return "bool"; + case TYPE_STRING: return "String"; + case TYPE_F32: return "float"; + case TYPE_F64: return "double"; + + case TYPE_CHAR: return "char"; + case TYPE_UCHAR: return "unsigned char"; + case TYPE_INT: return "int"; + case TYPE_UINT: return "unsigned int"; + case TYPE_LONG: return "long"; + case TYPE_ULONG: return "unsigned long"; + case TYPE_LLONG: return "long long"; + case TYPE_ULLONG: return "unsigned long long"; + case TYPE_SHORT: return "short"; + case TYPE_USHORT: return "unsigned short"; + + case TYPE_S8: return "int8_t"; + case TYPE_S16: return "int16_t"; + case TYPE_S32: return "int32_t"; + case TYPE_S64: return "int64_t"; + case TYPE_U8: return "uint8_t"; + case TYPE_U16: return "uint16_t"; + case TYPE_U32: return "uint32_t"; + case TYPE_U64: return "uint64_t"; + case TYPE_TYPE: return "int64_t"; + + case TYPE_INCOMPLETE: { + // This happens in case where we have a procedure in an away module. + // Meaning that it's not resolved fully on purpose (treeshaking). + // And that procedure takes a pointer from a struct that is never + // used elsewhere. In that case this seems fine. + return "void"; + } + invalid_default_case; + } + return ""; +} + +CORE_Static String +string_simple_decl_prefix(Ast_Type *ast) { + switch (ast->kind) { + case TYPE_POINTER: { + String string = string_simple_decl_prefix(ast->base); + string = pctx->fmt("%Q*", string); + return string; + } break; + case TYPE_LAMBDA: return {}; break; + case TYPE_ENUM: + case TYPE_ARRAY: { + return string_simple_decl_prefix(ast->base); + } break; + case TYPE_SLICE: { + String string = string_simple_decl_prefix(ast->base); + string = pctx->fmt("Slice%llu ", ast->type_id); + return string; + } break; + case TYPE_UNION: + case TYPE_STRUCT: { + auto constant = (Ast_Decl *)ast->ast; + auto name = constant->unique_name; + String string = pctx->fmt("%Q ", name); + String sc = {}; + return pctx->fmt("%Q%Q", sc, string); + } break; + default: { + String string = pctx->fmt("%s ", get_ctype_name_for_type(ast)); + return string; + } + } + return {}; +} + +CORE_Static String +string_simple_decl_postfix(Ast_Type *ast) { + switch (ast->kind) { + case TYPE_POINTER: + return string_simple_decl_postfix(ast->base); + break; + case TYPE_ARRAY: { + String result = string_simple_decl_postfix(ast->arr.base); + String string = pctx->fmt("[%d]%Q", ast->arr.size, result); + return string; + } break; + case TYPE_LAMBDA: break; + case TYPE_SLICE: + case TYPE_ENUM: + case TYPE_STRUCT: break; + default: + break; + // default: return string_from_cstring((char *)name(ast)); + } + return {}; +} + +CORE_Static String +string_simple_decl(Ast_Type *ast, Intern_String name = {}) { + if (ast->kind == TYPE_LAMBDA) { + String prefix = string_simple_decl_prefix(ast->func.ret); + String string = pctx->fmt("%Q(*%Q)(", prefix, name); + For(ast->func.args) { + String prefix_arg = string_simple_decl_prefix(it); + string = pctx->fmt("%Q%Q", string, prefix_arg); + if (&it != ast->func.args.end() - 1) + string = pctx->fmt("%Q, ", string); + } + string = pctx->fmt("%Q)", string); + return string; + } + else { + String string = string_simple_decl_prefix(ast); + if (name.len) { + string = pctx->fmt("%Q%Q", string, name); + } + String postfix = string_simple_decl_postfix(ast); + string = pctx->fmt("%Q%Q", string, postfix); + return string; + } +} + +CORE_Static void +gen_simple_decl(Ast_Type *ast, Intern_String name = {}) { + String string = string_simple_decl(ast, name); + gen("%Q", string); +} + +CORE_Static String +get_type_postfix(Ast_Type *type) { + switch (type->kind) { + case TYPE_F32: return "f"_s; break; + case TYPE_ULLONG: + case TYPE_U64: return "ULL"_s; break; + case TYPE_LLONG: + case TYPE_S64: return "LL"_s; break; + case TYPE_U8: + case TYPE_UCHAR: + case TYPE_U16: + case TYPE_UINT: + case TYPE_U32: return "U"_s; break; + + case TYPE_SHORT: + case TYPE_LONG: + case TYPE_F64: + case TYPE_S8: + case TYPE_S16: + case TYPE_S32: + case TYPE_INT: + case TYPE_CHAR: + return ""_s; + break; + invalid_default_case; + } + assert(!"Unhandled case or error"); + return ""_s; +} + +CORE_Static B32 +gen_value(Token *pos, Value a) { + if (is_untyped(a.type)) compiler_error(pos, "Internal compiler error: Untyped got propagated to the codegen stage"); + + B32 result = true; + Ast_Type *type = a.type; + if (is_enum(a.type)) { + type = a.type->base; + } + + switch (type->kind) { + CASE_INT : { + Scoped_Arena scratch(pctx->scratch); + String postfix = get_type_postfix(type); + const char *string = bigint_to_error_string(scratch.arena, &a.big_int_val, 10); + gen("%s%Q", string, postfix); + } break; + case TYPE_POINTER: { + if (a.type == pctx->type_pointer_to_char) { + gen("\"%Q\"", a.intern_val); + } + else { + U64 pointer_value = bigint_as_unsigned(&a.big_int_val); + gen("0x%llx", pointer_value); + } + } break; + CASE_BOOL : { + a.bool_val ? gen("true") : gen("false"); + } break; + CASE_FLOAT : { + String postfix = get_type_postfix(type); + gen("%f%Q", a.f64_val, postfix); + } break; + case TYPE_TYPE: { + gen("%d", a.type_val->type_id); + } break; + default: { + if (is_string(type)) { + int length = 0; + gen("CORE_ConditionalCompound(String){(uint8_t *)\""); + for (int i = 0; i < a.intern_val.len; i++) { + if (a.intern_val.str[i] == '\n') { + length += 2; + gen("\\n"); + } + else if (a.intern_val.str[i] == '\r') { + length += 2; + gen("\\r"); + } + else { + length += 1; + gen("%c", a.intern_val.str[i]); + } + } + gen("\", %d}", length); + } + else result = false; + } + } + return result; +} + +CORE_Static void +gen_defers_for_scope(Ast_Scope *scope) { + int save_line = last_line; + For(scope->defers.reverse()) { + gen("/* defer */"); + gen_stmt_scope(it->scope); + } + last_line = save_line; +} + +CORE_Static void +gen_defers_for_scope_and_parents(Ast_Scope *scope) { + for (; scope;) { + gen_defers_for_scope(scope); + scope = scope->parent_scope; + } +} + +CORE_Static void +gen_stmt_scope(Ast_Scope *scope, B32 switch_case_gen_break) { + gen("{"); + global_indent++; + For(scope->stmts) { + gen_line(it); + genln(""); + gen_ast(it); + } + gen_defers_for_scope(scope); + if (switch_case_gen_break == 1) genln("break;"); + global_indent--; + gen_last_line(); + genln("}"); +} + +enum { + ALWAYS_EMIT_VALUE = 0, + DONT_EMIT_VALUE = 1, +}; + +CORE_Static void +gen_pointer(Ast_Expr *expr) { + gen("&"); + if (is_flag_set(expr->flags, AST_IS_LVALUE)) { + gen_expr(expr); + } + else { + gen("("); + gen_simple_decl(expr->resolved_type); + gen(")"); + gen("{"); + gen_expr(expr); + gen("}"); + } +} + +CORE_Static void gen_try_any_or_slice(Ast_Expr *expr, Ast_Type *decl_type); +CORE_Static void +gen_compound(Ast *ast, bool generate_compound_decl = true) { + assert(ast->kind == AST_COMPOUND); + auto node = (Ast_Call *)ast; + + bool generated_anything = false; + bool is_global_variable = node->parent_scope->kind == AST_FILE; + if (!is_global_variable && generate_compound_decl) { + gen("("); + gen_simple_decl(node->resolved_type); + gen(")"); + } + gen("{"); + + // We need to double wrap it because Any is a pointer + // We need to pass it a compound array + if (is_slice(node->resolved_type)) { + generated_anything = true; + gen(".len = %d, ", node->exprs.len); + gen(".data = ("); + gen_simple_decl(node->resolved_type->base); + gen("[]"); + gen(")"); + gen("{"); + } + + For(node->exprs) { + generated_anything = true; + if (is_struct(node->resolved_type) || is_union(node->resolved_type)) + gen(".%Q = ", it->resolved_name); + else if (is_array(node->resolved_type)) + gen("[%d] = ", (int)it->resolved_index); + gen_try_any_or_slice(it->item, it->resolved_type); + + if (!node->exprs.is_last(it)) gen(", "); + } + + if (is_slice(node->resolved_type)) gen("}"); + if (!generated_anything) gen("0"); + gen("}"); +} + +CORE_Static void +gen_try_any_or_slice(Ast_Expr *expr, Ast_Type *decl_type) { + // We want normal values to get boxed as Any pointers + // but other then that we shouldn't box other Any values + if (expr->kind != AST_COMPOUND && is_any(decl_type) && !is_any(expr->resolved_type)) { + gen("(Any){"); + gen_pointer(expr); + gen(", %d}", expr->resolved_type->type_id); + } + else if (expr->kind == AST_IDENT && is_slice(decl_type)) { + Ast_Atom *atom = (Ast_Atom *)expr; + gen("("); + gen_simple_decl(decl_type); + gen(")"); + gen("{%d, ", atom->resolved_type->arr.size); + gen_expr(expr); + gen("}"); + } + else gen_expr(expr); +} + +CORE_Static void +gen_var(Ast_Decl *decl, B32 emit_value, B32 scope_names) { + if (is_flag_set(decl->flags, AST_FOREIGN)) gen("extern "); + gen_simple_decl(decl->type, decl->unique_name); + if (is_flag_set(decl->flags, AST_FOREIGN)) return; + + if (emit_value == DONT_EMIT_VALUE) { + return; + } + + if (decl->expr) { + if (is_array(decl->type)) { + gen_compound((Ast_Call *)decl->expr, false); + } + else { + gen(" = "); + gen_try_any_or_slice(decl->expr, decl->type); + } + } + else { // Default zero + if (is_numeric(decl->type)) { + gen(" = 0"); + } + else { + gen(" = {0}"); + } + } +} + +CORE_Static void +gen_lambda(Intern_String name, Ast_Lambda *lambda, B32 generate_block = true) { + gen_simple_decl(lambda->resolved_type->func.ret, name); + gen("("); + if (lambda->args.len == 0) gen("void"); + For(lambda->args) { + if (it->name == pctx->intern("..."_s)) { + gen("..."); + continue; + } + gen_var(it, DONT_EMIT_VALUE, true); + if (&it != (lambda->args.end() - 1)) + gen(", "); + } + gen(")"); + + if (generate_block && lambda->scope) { + gen_stmt_scope(lambda->scope); + } + else gen(";"); +} + +CORE_Static bool +gen_expr(Ast_Expr *ast) { + switch (ast->kind) { + CASE(IDENT, Atom) { + if (node->resolved_decl->kind == AST_NAMESPACE) + return false; + + bool is_global_variable = is_flag_set(node->resolved_decl->flags, AST_GLOBAL); + if (is_global_variable) { + gen("%Q", node->resolved_decl->unique_name); + } + + else { + gen("%Q", node->intern_val); + } + + BREAK(); + } + + CASE(VALUE, Atom) { + B32 written = gen_value(node->pos, node->value); + if (!written) { + gen("%Q", node->value.intern_val); + } + BREAK(); + } + + CASE(ARRAY, Array) { + gen("%d", node->resolved_type->type_id); + BREAK(); + } + + CASE(INDEX, Index) { + gen("("); + gen_expr(node->expr); + if (node->index_original_type == pctx->type_string || node->index_original_type == pctx->untyped_string) { + gen(".data"); + } + else if (is_slice(node->index_original_type)) { + gen(".data"); + } + gen("["); + gen_expr(node->index); + gen("]"); + gen(")"); + BREAK(); + } + + CASE(LAMBDA_EXPR, Lambda) { + gen_lambda({}, node); + BREAK(); + } + + CASE(BINARY, Binary) { + if (node->op == TK_Dot) { + if (gen_expr(node->left)) { + if (node->dot_access_step_resolution && node->dot_access_step_resolution->kind == TYPE_POINTER) gen("->"); + else gen("."); + } + gen_expr(node->right); + return true; + } + + else if (node->op == TK_Arrow) { + gen("("); + gen("("); + gen_simple_decl(node->resolved_type); + gen(")"); + gen_expr(node->left); + gen(")"); + return true; + } + + else if (node->resolved_operator_overload) { + gen("%Q(", node->resolved_operator_overload->unique_name); + gen_expr(node->left); + gen(", "); + gen_expr(node->right); + gen(")"); + } + + else { + if (!token_is_assign(node->op)) gen("("); + gen_expr(node->left); + gen("%s", name(node->op)); + gen_expr(node->right); + if (!token_is_assign(node->op)) gen(")"); + } + + BREAK(); + } + + CASE(UNARY, Unary) { + if (node->resolved_operator_overload) { + gen("%Q(", node->resolved_operator_overload->unique_name); + gen_expr(node->expr); + gen(")"); + } + + else { + gen("("); + if (node->op != TK_PostIncrement && node->op != TK_PostDecrement) gen("%s", name(node->op)); + gen_expr(node->expr); + if (node->op == TK_PostIncrement || node->op == TK_PostDecrement) gen("%s", name(node->op)); + gen(")"); + } + BREAK(); + } + + CASE(VAR, Decl) { + gen_ast(node); + BREAK(); + } + + CASE(LENGTH_OF, Call) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + gen_expr(expr); + if (is_pointer(expr->resolved_type)) + gen("->len"); + else gen(".len"); + BREAK(); + } + + CASE(CALL, Call) { + gen("%Q(", node->resolved_decl->unique_name); + For(node->exprs) { + gen_try_any_or_slice(it->item, it->resolved_type); + if (!node->exprs.is_last(it)) gen(", "); + } + gen(")"); + BREAK(); + } + + CASE(COMPOUND, Call) { + gen_compound(node); + BREAK(); + } + + invalid_default_case; + } + return true; +} + +/* +- [ ] Assigning arrays to arrays doesn't work in C. Being able to do that seems like a good idea though + +*/ + +CORE_Static void +gen_ast(Ast *ast) { + switch (ast->kind) { + + CASE(RUNTIME_ASSERT, Builtin) { + if (node->assert_message.len == 0) gen("Assert"); + else gen("AssertMessage"); + gen("("); + gen_expr(node->expr); + if (node->assert_message.len) { + gen(", \"%Q\"", node->assert_message); + } + gen(");"); + BREAK(); + } + + CASE(RETURN, Return) { + gen_defers_for_scope_and_parents(node->parent_scope); + gen("return "); + if (node->expr) gen_expr(node->expr); + gen(";"); + BREAK(); + } + + CASE(VAR, Decl) { + gen_var(node, is_inside_struct ? DONT_EMIT_VALUE : ALWAYS_EMIT_VALUE, true); + if (!is_flag_set(ast->flags, AST_EXPR)) gen(";"); + BREAK(); + } + + CASE(IF, If) { + For(node->ifs) { + gen_line(it); + genln(""); + if (it->init) { + gen_expr(it->init); + gen(";"); + genln(""); + } + if (node->ifs.is_first(it)) { + gen("if("); + gen_expr(it->expr); + gen(")"); + gen_stmt_scope(it->scope); + } + else { + gen("else"); + if (it->expr) { + gen(" if("); + gen_expr(it->expr); + gen(")"); + } + gen_stmt_scope(it->scope); + } + } + BREAK(); + } + + case AST_GOTO: { + auto n = (Ast_Goto *)ast; + gen("goto %Q;", n->label); + } break; + + case AST_LABEL: { + auto n = (Ast_Label *)ast; + if (n->enable_goto == false) gen("/* %Q */", n->name); + if (n->enable_goto == true) gen("%Q:", n->name); + gen_stmt_scope(n->scope); + } break; + + case AST_CONTINUE: { + gen_defers_for_scope(ast->parent_scope); + gen("continue;"); + } break; + + case AST_BREAK: { + gen_defers_for_scope(ast->parent_scope); + gen("break;"); + } break; + + case AST_DEFER: { + gen("// defer"); + } break; + + case AST_COMPILER_BREAKPOINT_STMT: { + __debugbreak(); + } break; + + case AST_PASS: { + gen("// pass"); + } break; + + CASE(BINARY, Binary) { + gen_expr(node); + gen(";"); + BREAK(); + } + + CASE(FOR, For) { + // Array iter + if (node->is_array_traversal) { + gen("for(int64_t _i%d = 0; _i%d < ", node->pos->line, node->pos->line); + if (is_array(node->cond->resolved_type)) { + gen("BufferSize("); + gen_expr(node->cond); + gen(")"); + } + else { + assert(is_slice(node->cond->resolved_type)); + gen_expr(node->cond); + gen(".len"); + } + + gen("; _i%d+=1)", node->pos->line); + gen("{"); + global_indent++; + genln(""); + gen_simple_decl(node->array_traversal_var->type, node->array_traversal_var->name); + gen(" = "); + gen_expr(node->cond); + if (node->is_also_slice_traversal) gen(".data"); + gen(" + _i%d;", node->pos->line); + For(node->scope->stmts) { + gen_line(it); + genln(""); + gen_ast(it); + } + global_indent--; + gen_last_line(); + gen_defers_for_scope(ast->parent_scope); + genln("}"); + } + + // Normal for loop + else { + gen("for("); + if (node->init) gen_expr(node->init); + gen(";"); + if (node->cond) gen_expr(node->cond); + gen(";"); + if (node->iter) gen_expr(node->iter); + gen(")"); + gen_stmt_scope(node->scope); + } + + BREAK(); + } + + CASE(LAMBDA, Decl) { + gen_line(node); + genln(""); + + if (is_flag_set(node->expr->flags, AST_FOREIGN)) { + gen("/*foreign*/"); + } + gen_lambda(node->unique_name, node->lambda); + BREAK(); + } + + case AST_UNION: + CASE(STRUCT, Decl) { + gen("%s ", node->kind == AST_STRUCT ? "struct" : "union"); + gen("%Q{", node->unique_name); + global_indent++; + is_inside_struct++; + For(node->scope->decls) { + genln(""); + gen_ast(it); + } + + is_inside_struct--; + global_indent--; + genln("};"); + BREAK(); + } + + CASE(ENUM, Decl) { + gen("/*enum %Q{", node->unique_name); + // @todo add typespec + global_indent++; + For(node->scope->decls) { + genln("%Q", it->unique_name); + gen(" = "); + gen_value(it->pos, it->value); + gen(","); + } + global_indent--; + genln("};*/"); + BREAK(); + } + + case AST_TYPE: + CASE(CONST, Decl) { + unused(node); + BREAK(); + } + + CASE(SWITCH, Switch) { + gen("switch("); + gen_expr(node->value); + gen("){"); + global_indent++; + + For(node->cases) { + For2(it->labels, label) { + gen_line(it); + genln(""); + gen("case "); + gen_expr(label); + gen(":"); + } + + gen_stmt_scope(it->scope, it->fallthrough ? false : true); + } + + if (node->default_scope) { + genln("default: "); + gen_stmt_scope(node->default_scope); + } + + global_indent--; + gen_last_line(); + genln("}"); + + BREAK(); + } + + CASE(VAR_UNPACK, Var_Unpack) { + Ast_Type *t = node->resolved_type; + For(node->vars) { + gen_ast(it); + } + + Intern_String var_name = get_unique_name(); + gen_simple_decl(node->resolved_type, var_name); + gen(" = "); + gen_expr(node->expr); + gen(";"); + + int i = 0; + For(node->vars) { + Ast_Resolved_Member &m = t->agg.members[i]; + char *p = "&"; + if (is_array(m.type)) p = ""; + + gen("MemoryCopy((void *)%s%Q, (void *)%s%Q.%Q, sizeof(%Q));", p, it->name, p, var_name, m.name, it->name); + i = +1; + } + BREAK(); + } + + case AST_CONSTANT_ASSERT: + case AST_NAMESPACE: break; + + default: { + assert(is_flag_set(ast->flags, AST_EXPR)); + gen_expr((Ast_Expr *)ast); + gen(";"); + } + } +} + +CORE_Static String +compile_to_c_code() { + pctx->time.code_generation = os_time(); + + if (pctx->single_header_library_mode) { + gen(R"( +/* + Do this: + #define %Q_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define %Q_IMPLEMENTATION + #include "%Q.h" + + You can #define Assert(x) to avoid using default assert + You can #define AssertMessage(x) to get more comprehensive error info + You can #define MemoryCopy(x) to avoid using default memory copy +*/ + )", + pctx->single_header_library_name, pctx->single_header_library_name, pctx->single_header_library_name); + genln("#ifndef %Q_LIBRARY_HEADER ", pctx->single_header_library_name); + genln("#define %Q_LIBRARY_HEADER ", pctx->single_header_library_name); + } + + gen(R"( + +#include +#include +#include + +int printf(const char *format, ...); +#ifndef Assert + #define Assert(x) do{ \ + if(!(x)) { \ + printf("Assertion failed! %%s(%%d): %%s\n", __FILE__, __LINE__, #x); \ + *(volatile int *)0 = 0; \ + } \ + }while(0) +#endif + +#ifndef AssertMessage + #define AssertMessage(x,...) Assert(x) +#endif + +#ifndef MemoryCopy + #define MemoryCopy MemoryCopy_ + + static void + MemoryCopy_(void *dst, void *src, size_t size){ + uint8_t *d = (uint8_t*)dst; + uint8_t *s = (uint8_t*)src; + for(size_t i = 0; i < size; i++){ + d[i] = s[i]; + } + } +#endif + +#define BufferSize(x) ((int)(sizeof(x)/sizeof((x)[0]))) + +#ifdef __clang__ + #define CORE_ConditionalCompound(x) (x) +#elif _MSC_VER + #define CORE_ConditionalCompound(x) +#else + #define CORE_ConditionalCompound(x) (x) +#endif + +)"); + + // Generate struct forward decls + For(pctx->ordered_decls) { + if (it->kind == AST_STRUCT) { + genln("typedef struct %Q %Q;", it->unique_name, it->unique_name); + } + else if (it->kind == AST_UNION) { + genln("typedef union %Q %Q;", it->unique_name, it->unique_name); + } + } + + // Generate slice types + For2(pctx->all_types, type) { + if (type->kind == TYPE_SLICE) { + genln("typedef struct Slice%llu{", type->type_id); + global_indent++; + genln("int64_t len;"); + genln(""); + gen_simple_decl(type_pointer(type->base), pctx->intern("data"_s)); + gen(";"); + global_indent--; + genln("} Slice%llu;", type->type_id); + } + } + + Intern_String intern_main = pctx->intern("main"_s); + Intern_String intern_win_main = pctx->intern("WinMain"_s); + + Ast_Decl *main = 0; + Ast_Decl *win_main = 0; + + // Generate lambda forward decls + For(pctx->ordered_decls) { + if (it->kind == AST_LAMBDA) { + if (it->name == intern_main) { + main = it; + it->unique_name = it->name; + } + + if (it->name == intern_win_main) { + win_main = it; + it->unique_name = it->name; + } + + genln(""); + + gen_lambda(it->unique_name, it->lambda, false); + } + } + + if (!main && !win_main) { + compiler_error(0, "Entry point is not defined! Try main or WinMain"); + } + + if (pctx->emit_type_info) { + // Generate language.core + for (S32 i = 0; i < pctx->base_language_ordered_decl_len; i++) { + Ast_Decl *it = get(&pctx->ordered_decls, i); + genln(""); + gen_ast(it); + } + + // Generate type info + genln("int type_infos_len = %d;", length(&pctx->all_types)); + genln("Type_Info *type_infos = (Type_Info[]){"); + global_indent++; + int i = 0; + For2(pctx->all_types, t) { + if (t->kind == TYPE_POLYMORPH) continue; + if (i++ != t->type_id) { + compiler_error(0, "Internal compiler error: type info array is inconsistent"); + } + + genln("{/*%Q*/", typestring(t)); + global_indent += 1; + genln(".kind = %d, ", (S32)t->kind); + genln(".size = %d, ", (S32)t->size); + genln(".align = %d, ", (S32)t->align); + genln(".is_unsigned = %s, ", t->is_unsigned ? "true" : "false"); + genln(".type = %d, ", t->type_id); + + switch (t->kind) { + case TYPE_POINTER: + case TYPE_SLICE: { + genln(".base_type = %d", t->base->type_id); + } break; + case TYPE_ARRAY: { + genln(".base_type = %d, ", t->base->type_id); + genln(".array_size = %d", t->arr.size); + } break; + case TYPE_LAMBDA: { + if (t->func.args.len == 0) goto end_of_switch; + genln(".lambda_return = %d, ", t->func.ret->type_id); + genln(".lambda_arguments.len = %d, ", t->func.args.len); + genln(".lambda_arguments.data = (Type_Info[%d]){", t->func.args.len); + global_indent += 1; + For2(t->func.args, arg) { + genln("{.type = %d}, ", arg->type_id); + } + global_indent -= 1; + genln("}"); + } break; + case TYPE_UNION: + case TYPE_STRUCT: { + if (t->agg.members.len == 0) goto end_of_switch; + genln(".struct_members.len = %d, ", t->agg.members.len); + genln(".struct_members.data = (Type_Info_Struct_Member[%d]){", t->agg.members.len); + global_indent += 1; + For2(t->agg.members, m) { + genln("{.name = {(uint8_t *)\"%Q\", %d}, .type = %d, .offset = %d}, ", m.name, m.name.len, m.type->type_id, m.offset); + } + global_indent -= 1; + genln("}"); + } break; + case AST_ENUM: { + // todo; + } break; + default: { + } + } + end_of_switch:; + global_indent -= 1; + genln("},"); + } + global_indent--; + genln("};"); + } + + if (pctx->single_header_library_mode) { + genln("#endif"); + genln("#ifdef %Q_IMPLEMENTATION ", pctx->single_header_library_name); + } + + // Generate actual code + int index = 0; + For(pctx->ordered_decls) { + if (index >= pctx->base_language_ordered_decl_len) { + genln(""); + gen_ast(it); + } + index += 1; + } + + if (pctx->single_header_library_mode) { + genln("#endif"); + } + + String string_result = string_flatten(pctx->perm, &pctx->gen); + pctx->time.code_generation = os_time() - pctx->time.code_generation; + return string_result; +} diff --git a/core_compiler.cpp b/src/language/core_compiler.cpp similarity index 97% rename from core_compiler.cpp rename to src/language/core_compiler.cpp index 368b3b8..978db1b 100644 --- a/core_compiler.cpp +++ b/src/language/core_compiler.cpp @@ -1,489 +1,489 @@ - -// apifn -static void core_free_compiler() { - deallocate(pctx->heap, pctx->type_map.data); - deallocate(pctx->heap, pctx->interns.map.data); - arena_release(pctx->stage_arena); - arena_release(pctx->scratch); - arena_release(pctx->perm); -} - -// apifn -static void core_init_compiler(Core_Ctx *ctx, Allocator *allocator) { - ctx->time.init_context = os_time(); - pctx = ctx; - - ctx->emit_type_info = true; - ctx->emit_line_directives = true; - ctx->debugger_break_on_compiler_error = true; - ctx->same_scope_token = {SAME_SCOPE}; - - arena_init(&ctx->perm_push_only, "Perm Push Only"_s); - arena_init(&ctx->scratch_, "Scratch"_s); - arena_init(&ctx->stage_arena_, "Stage Arena"_s); - arena_init(&ctx->token_arena, "Token Arena"_s); - ctx->scratch = &ctx->scratch_; - ctx->stage_arena = &ctx->stage_arena_; - ctx->perm = &ctx->perm_push_only; - ctx->heap = allocator; - ctx->type_map = map_make(ctx->heap, 2048); - ctx->gen = {ctx->perm}; - ctx->helper_builder = {ctx->perm}; - ctx->scope_ids = 1; - bigint_allocator = ctx->perm; - - ctx->interns = intern_table_make(ctx->perm, ctx->heap, 2048); - - /*#import meta -for i in meta.keywords: - print(f'pctx->keyword_{i.lower()} = pctx->intern("{i}"_s);') -print(f'pctx->interns.first_keyword = pctx->keyword_{meta.keywords[0].lower()}.str;') -print(f'pctx->interns.last_keyword = pctx->keyword_{meta.keywords[-1].lower()}.str;') - -for i in meta.interns: - print(f'pctx->intern_{i.lower()} = pctx->intern("{i}"_s);') - -index = 0 -for i in meta.token_simple_expr: - if i[1] != "SPECIAL": - print(f'pctx->op_info_table[{index}] = {{pctx->intern("{i[1]}"_s), "{i[0].upper()}"_s, TK_{i[0]}, {int(i[2]&meta.BINARY_EXPR>0)}, {int(i[2]&meta.UNARY_EXPR>0)}}};') - index += 1 - - */ - pctx->keyword_struct = pctx->intern("struct"_s); - pctx->keyword_union = pctx->intern("union"_s); - pctx->keyword_true = pctx->intern("true"_s); - pctx->keyword_default = pctx->intern("default"_s); - pctx->keyword_continue = pctx->intern("continue"_s); - pctx->keyword_break = pctx->intern("break"_s); - pctx->keyword_false = pctx->intern("false"_s); - pctx->keyword_return = pctx->intern("return"_s); - pctx->keyword_switch = pctx->intern("switch"_s); - pctx->keyword_assert = pctx->intern("Assert"_s); - pctx->keyword_if = pctx->intern("if"_s); - pctx->keyword_elif = pctx->intern("elif"_s); - pctx->keyword_pass = pctx->intern("pass"_s); - pctx->keyword_else = pctx->intern("else"_s); - pctx->keyword_for = pctx->intern("for"_s); - pctx->keyword_enum = pctx->intern("enum"_s); - pctx->keyword_goto = pctx->intern("goto"_s); - pctx->keyword_defer = pctx->intern("defer"_s); - pctx->interns.first_keyword = pctx->keyword_struct.str; - pctx->interns.last_keyword = pctx->keyword_defer.str; - pctx->intern_typeof = pctx->intern("typeof"_s); - pctx->intern_sizeof = pctx->intern("sizeof"_s); - pctx->intern_len = pctx->intern("Len"_s); - pctx->intern_alignof = pctx->intern("alignof"_s); - pctx->intern_foreign = pctx->intern("foreign"_s); - pctx->intern_strict = pctx->intern("strict"_s); - pctx->intern_void = pctx->intern("void"_s); - pctx->intern_flag = pctx->intern("flag"_s); - pctx->intern_it = pctx->intern("it"_s); - pctx->intern_load = pctx->intern("load"_s); - pctx->intern_import = pctx->intern("import"_s); - pctx->intern_link = pctx->intern("link"_s); - pctx->intern_compiler_breakpoint = pctx->intern("compiler_breakpoint"_s); - pctx->op_info_table[0] = {pctx->intern("*"_s), "MUL"_s, TK_Mul, 1, 0}; - pctx->op_info_table[1] = {pctx->intern("/"_s), "DIV"_s, TK_Div, 1, 0}; - pctx->op_info_table[2] = {pctx->intern("%"_s), "MOD"_s, TK_Mod, 1, 0}; - pctx->op_info_table[3] = {pctx->intern("<<"_s), "LEFTSHIFT"_s, TK_LeftShift, 1, 0}; - pctx->op_info_table[4] = {pctx->intern(">>"_s), "RIGHTSHIFT"_s, TK_RightShift, 1, 0}; - pctx->op_info_table[5] = {pctx->intern("+"_s), "ADD"_s, TK_Add, 1, 1}; - pctx->op_info_table[6] = {pctx->intern("-"_s), "SUB"_s, TK_Sub, 1, 1}; - pctx->op_info_table[7] = {pctx->intern("=="_s), "EQUALS"_s, TK_Equals, 1, 0}; - pctx->op_info_table[8] = {pctx->intern("<="_s), "LESSERTHENOREQUAL"_s, TK_LesserThenOrEqual, 1, 0}; - pctx->op_info_table[9] = {pctx->intern(">="_s), "GREATERTHENOREQUAL"_s, TK_GreaterThenOrEqual, 1, 0}; - pctx->op_info_table[10] = {pctx->intern("<"_s), "LESSERTHEN"_s, TK_LesserThen, 1, 0}; - pctx->op_info_table[11] = {pctx->intern(">"_s), "GREATERTHEN"_s, TK_GreaterThen, 1, 0}; - pctx->op_info_table[12] = {pctx->intern("!="_s), "NOTEQUALS"_s, TK_NotEquals, 1, 0}; - pctx->op_info_table[13] = {pctx->intern("&"_s), "BITAND"_s, TK_BitAnd, 1, 0}; - pctx->op_info_table[14] = {pctx->intern("|"_s), "BITOR"_s, TK_BitOr, 1, 0}; - pctx->op_info_table[15] = {pctx->intern("^"_s), "BITXOR"_s, TK_BitXor, 1, 0}; - pctx->op_info_table[16] = {pctx->intern("&&"_s), "AND"_s, TK_And, 1, 0}; - pctx->op_info_table[17] = {pctx->intern("||"_s), "OR"_s, TK_Or, 1, 0}; - pctx->op_info_table[18] = {pctx->intern("~"_s), "NEG"_s, TK_Neg, 0, 1}; - pctx->op_info_table[19] = {pctx->intern("!"_s), "NOT"_s, TK_Not, 0, 1}; - /*END*/ - - // Init types - pctx->type__void = {TYPE_VOID}; - pctx->type__bool = {TYPE_BOOL, sizeof(bool), __alignof(bool)}; - pctx->type__type = {TYPE_TYPE, sizeof(S64), __alignof(S64)}; - - pctx->type__f32 = {TYPE_F32, sizeof(F32), __alignof(F32)}; - pctx->type__f64 = {TYPE_F64, sizeof(F64), __alignof(F64)}; - - pctx->type__s8 = {TYPE_S8, sizeof(S8), __alignof(S8)}; - pctx->type__s16 = {TYPE_S16, sizeof(S16), __alignof(S16)}; - pctx->type__s32 = {TYPE_S32, sizeof(S32), __alignof(S32)}; - pctx->type__s64 = {TYPE_S64, sizeof(S64), __alignof(S64)}; - - pctx->type__u8 = {TYPE_U8, sizeof(U8), __alignof(U8), true}; - pctx->type__u16 = {TYPE_U16, sizeof(U16), __alignof(U16), true}; - pctx->type__u32 = {TYPE_U32, sizeof(U32), __alignof(U32), true}; - pctx->type__u64 = {TYPE_U64, sizeof(U64), __alignof(U64), true}; - - pctx->type__untyped_bool = {TYPE_UNTYPED_BOOL, sizeof(bool), __alignof(bool)}; - pctx->type__untyped_int = {TYPE_UNTYPED_INT, sizeof(S64), __alignof(S64)}; - pctx->type__untyped_string = {TYPE_UNTYPED_STRING, sizeof(String), __alignof(String)}; - pctx->type__untyped_float = {TYPE_UNTYPED_FLOAT, sizeof(double), __alignof(double)}; - - pctx->type__char = {TYPE_CHAR, sizeof(char), __alignof(char)}; - pctx->type__uchar = {TYPE_UCHAR, sizeof(unsigned char), __alignof(unsigned char), true}; - pctx->type__int = {TYPE_INT, sizeof(int), __alignof(int)}; - pctx->type__uint = {TYPE_UINT, sizeof(unsigned), __alignof(unsigned), true}; - pctx->type__long = {TYPE_LONG, sizeof(long), __alignof(long)}; - pctx->type__ulong = {TYPE_ULONG, sizeof(unsigned long), __alignof(unsigned long), true}; - pctx->type__llong = {TYPE_LLONG, sizeof(long long), __alignof(long long)}; - pctx->type__ullong = {TYPE_ULLONG, sizeof(unsigned long long), __alignof(unsigned long long), true}; - pctx->type__short = {TYPE_SHORT, sizeof(short), __alignof(short)}; - pctx->type__ushort = {TYPE_USHORT, sizeof(unsigned short), __alignof(unsigned short), true}; - - pctx->type_char = &pctx->type__char; - pctx->type_uchar = &pctx->type__uchar; - pctx->type_int = &pctx->type__int; - pctx->type_uint = &pctx->type__uint; - pctx->type_long = &pctx->type__long; - pctx->type_ulong = &pctx->type__ulong; - pctx->type_llong = &pctx->type__llong; - pctx->type_ullong = &pctx->type__ullong; - pctx->type_short = &pctx->type__short; - pctx->type_ushort = &pctx->type__ushort; - - pctx->type_void = &pctx->type__void; - - // pctx->type__string = {TYPE_STRING, sizeof(String), __alignof(String)}; - // pctx->type_any; // Needs to be inited at runtime - // pctx->type_string = &pctx->type__string; - - pctx->type_type = &pctx->type__type; - pctx->type_bool = &pctx->type__bool; - - pctx->type_f32 = &pctx->type__f32; - pctx->type_f64 = &pctx->type__f64; - - pctx->type_s8 = &pctx->type__s8; - pctx->type_s16 = &pctx->type__s16; - pctx->type_s32 = &pctx->type__s32; - pctx->type_s64 = &pctx->type__s64; - - pctx->type_u8 = &pctx->type__u8; - pctx->type_u16 = &pctx->type__u16; - pctx->type_u32 = &pctx->type__u32; - pctx->type_u64 = &pctx->type__u64; - - pctx->untyped_string = &pctx->type__untyped_string; - pctx->untyped_bool = &pctx->type__untyped_bool; - pctx->untyped_int = &pctx->type__untyped_int; - pctx->untyped_float = &pctx->type__untyped_float; - - pctx->type__vargs = {TYPE_VARGS}; - pctx->type_vargs = &pctx->type__vargs; - - pctx->type_pointer_to_char = type_pointer(pctx->type_char); - pctx->type_pointer_to_void = type_pointer(pctx->type_void); - - // Init paths - ctx->exe_folder = os_get_exe_dir(ctx->perm); - ctx->working_folder = os_get_working_dir(ctx->perm); - - String main_module = string_fmt(ctx->perm, "%Q/modules", ctx->exe_folder); - add(ctx->perm, &ctx->module_folders, main_module); - ctx->time.init_context = os_time() - ctx->time.init_context; -} - -// apifn -CORE_Static void -core_bootstrap_compiler(Allocator *allocator) { - Core_Ctx *ctx = allocate_struct(allocator, Core_Ctx); - core_init_compiler(ctx, allocator); -} - -CORE_Static void -insert_builtin_type_into_scope(Ast_Scope *p, String name, Ast_Type *type) { - Intern_String string = pctx->intern(name); - Ast_Decl *decl = ast_type(0, string, type); - type->type_id = pctx->type_ids++; - decl->parent_scope = p; - decl->state = DECL_RESOLVED; - insert_into_scope(p, decl); - add(pctx->perm, &pctx->all_types, type); -} - -CORE_Static void -parse_all_modules() { - pctx->time.parsing = os_time(); - - For2(pctx->modules, module) { - if (module->state != MODULE_REGISTERED) - continue; - - For(module->all_loaded_files) { - parse_file(it); - } - - if (module != pctx->language_base_module) { - add(pctx->perm, &module->implicit_imports, (Ast_Scope *)pctx->language_base_module); - } - - module->state = MODULE_PARSED; - } - pctx->time.parsing = os_time() - pctx->time.parsing; -} - -CORE_Static Ast_Module * -add_module(Token *pos, Intern_String filename, B32 command_line_module, bool string_only_module) { - Arena *scratch = pctx->scratch; - Scoped_Arena _scope(scratch); - String absolute_file_path = {}; - String absolute_base_folder = {}; - - if (string_only_module) { - absolute_file_path = filename.s; - absolute_base_folder = filename.s; - } - else { - // - // Find in working directory - // - if (command_line_module) { - if (os_does_file_exist(filename.s)) { - String path = os_get_absolute_path(scratch, filename.s); - string_path_normalize(path); - absolute_file_path = string_copy(scratch, path); - absolute_base_folder = string_chop_last_slash(path); - } - } - - // - // Find in module folder - // - else { - For(pctx->module_folders) { - String path = string_fmt(scratch, "%Q/%Q", it, filename); - if (os_does_file_exist(path)) { - absolute_file_path = path; - absolute_base_folder = string_chop_last_slash(path); - break; - } - } - } - - if (absolute_file_path.len == 0) { - compiler_error(pos, "Couldn't find the module with name %Q", filename); - } - } - - For(pctx->modules) { - if (string_compare(it->absolute_file_path, absolute_file_path)) { - log_trace("Returning registered module: %Q\n", absolute_file_path); - return it; - } - } - - log_trace("Adding module: %Q\n", filename); - Ast_Module *result = ast_new(Ast_Module, AST_MODULE, pos, 0); - result->absolute_file_path = string_copy(pctx->perm, absolute_file_path); - result->absolute_base_folder = string_copy(pctx->perm, absolute_base_folder); - result->debug_name = string_skip_to_last_slash(result->absolute_file_path); - result->module = result; // @warning: self referential - result->file = result; // @warning: self referential - result->parent_scope = 0; - result->scope_id = pctx->scope_ids++; - - register_ast_file(pos, result->absolute_file_path, result, GLOBAL_IMPLICIT_LOAD); - add(pctx->perm, &pctx->modules, result); - return result; -} - -CORE_Static Ast_File * -custom_parse_string(String string) { - Ast_Module *module = pctx->custom_module; - Ast_File *file = get(&module->all_loaded_files, 0); - file->filecontent = string; - parse_file(file); - return file; -} - -CORE_Static void -resolve_everything_in_module(Ast_Module *module) { - if (module->state == MODULE_RESOLVED) - return; - pctx->time.typechecking = os_time(); - For2(module->all_loaded_files, file) { - For2(file->decls, decl) { - if (decl->flags & AST_POLYMORPH) continue; - - resolve_decl(decl); - if (decl->kind == AST_STRUCT || decl->kind == AST_UNION) type_complete(decl->type_val); - } - } - module->state = MODULE_RESOLVED; - pctx->time.typechecking = os_time() - pctx->time.typechecking; -} - -CORE_Static void -init_language_core() { - pctx->custom_module = add_module(0, pctx->intern("Custom.core"_s), false, true); - pctx->custom_module->state = MODULE_RESOLVED; - - Ast_Module *module = add_module(0, pctx->intern("Language.core"_s), false, true); - get(&module->all_loaded_files, 0)->filecontent = - R"( -Any :: struct - data: *void - type: Type - -String :: struct - data: *U8 - len: int - -Two :: struct($A: Type, $B: Type) - a: A - b: B - -Type_Info_Kind :: enum - S64 // FIRST_NUMERIC - S32 - S16 - S8 - INT - CHAR - U64 - U32 - U16 - U8 - F32 - F64 - POINTER - BOOL // LAST_NUMERIC - STRING - VOID - ARRAY - LAMBDA - STRUCT - UNION - ENUM - TYPE - SLICE - TUPLE - -Type_Info_Struct_Member :: struct - name: String - type: Type - offset: int - -Type_Info :: struct - kind: Type_Info_Kind - size: int - align: int - is_unsigned: bool - type: Type - - base_type: Type - array_size: int - struct_members: []Type_Info_Struct_Member - lambda_arguments: []Type_Info - lambda_return: Type - -type_infos_len: int #foreign -type_infos : *Type_Info #foreign - -GetTypeInfo :: (type: Type): *Type_Info - id := type->int - if id >= type_infos_len - return 0 - return type_infos + id - )"_s; - - { - - insert_builtin_type_into_scope(module, "char"_s, pctx->type_char); - insert_builtin_type_into_scope(module, "uchar"_s, pctx->type_uchar); - insert_builtin_type_into_scope(module, "int"_s, pctx->type_int); - insert_builtin_type_into_scope(module, "uint"_s, pctx->type_uint); - insert_builtin_type_into_scope(module, "long"_s, pctx->type_long); - insert_builtin_type_into_scope(module, "ulong"_s, pctx->type_ulong); - insert_builtin_type_into_scope(module, "llong"_s, pctx->type_llong); - insert_builtin_type_into_scope(module, "ullong"_s, pctx->type_ullong); - insert_builtin_type_into_scope(module, "short"_s, pctx->type_short); - insert_builtin_type_into_scope(module, "ushort"_s, pctx->type_ushort); - - insert_builtin_type_into_scope(module, "S64"_s, pctx->type_s64); - insert_builtin_type_into_scope(module, "S32"_s, pctx->type_s32); - insert_builtin_type_into_scope(module, "S16"_s, pctx->type_s16); - insert_builtin_type_into_scope(module, "S8"_s, pctx->type_s8); - - insert_builtin_type_into_scope(module, "U64"_s, pctx->type_u64); - insert_builtin_type_into_scope(module, "U32"_s, pctx->type_u32); - insert_builtin_type_into_scope(module, "U16"_s, pctx->type_u16); - insert_builtin_type_into_scope(module, "U8"_s, pctx->type_u8); - insert_builtin_type_into_scope(module, "F64"_s, pctx->type_f64); - insert_builtin_type_into_scope(module, "F32"_s, pctx->type_f32); - insert_builtin_type_into_scope(module, "void"_s, pctx->type_void); - insert_builtin_type_into_scope(module, "bool"_s, pctx->type_bool); - // insert_builtin_type_into_scope(module, "String"_s, pctx->type_string); - insert_builtin_type_into_scope(module, "Type"_s, pctx->type_type); - } - - { - Ast_File *file = get(&module->all_loaded_files, 0); - Ast_Scope *scope = ast_decl_scope(&pctx->null_token, pctx->perm, file); - scope->parent_scope = file; - scope->module = module; - Ast_Decl *decl = ast_namespace(&pctx->null_token, scope, pctx->intern("Const"_s)); - decl->state = DECL_RESOLVED; - - Value v1 = {}; - v1.type = pctx->untyped_string; - v1.intern_val = pctx->intern(OS_NAME); - Ast_Decl *const_os1 = ast_const(&pctx->null_token, pctx->intern("OSName"_s), v1); - const_os1->state = DECL_RESOLVED; - insert_into_scope(scope, const_os1); - - Value v2 = {}; - v1.type = pctx->untyped_string; - v1.intern_val = pctx->intern(OS_NAME_LOWER); - Ast_Decl *const_os2 = ast_const(&pctx->null_token, pctx->intern("OSNameLower"_s), v2); - const_os2->state = DECL_RESOLVED; - insert_into_scope(scope, const_os2); - - insert_into_scope(module, decl); - } - - pctx->language_base_module = module; - - parse_all_modules(); - resolve_everything_in_module(module); - - // @note: language stuff needs to be declared before type_info data - // so we mark where it ends - pctx->base_language_ordered_decl_len = length(&pctx->ordered_decls); - Ast_Decl *any_decl = search_for_single_decl(module, pctx->intern("Any"_s)); - assert(any_decl->type == pctx->type_type); - pctx->type_any = any_decl->type_val; - - Ast_Decl *string_decl = search_for_single_decl(module, pctx->intern("String"_s)); - assert(string_decl->type == pctx->type_type); - pctx->type_string = string_decl->type_val; -} - -CORE_Static String -compile_file_to_string(Allocator *allocator, String filename) { - F64 total_time = os_time(); - core_bootstrap_compiler(allocator); - pctx->time.total = total_time; - pctx->time.start = total_time; - init_language_core(); - - Ast_Module *module = add_module(0, pctx->intern(filename), true); - parse_all_modules(); - assert(module); - - resolve_everything_in_module(module); - - String result = compile_to_c_code(); - - pctx->time.total = os_time() - pctx->time.total; - return result; -} + +// apifn +static void core_free_compiler() { + deallocate(pctx->heap, pctx->type_map.data); + deallocate(pctx->heap, pctx->interns.map.data); + arena_release(pctx->stage_arena); + arena_release(pctx->scratch); + arena_release(pctx->perm); +} + +// apifn +static void core_init_compiler(Core_Ctx *ctx, Allocator *allocator) { + ctx->time.init_context = os_time(); + pctx = ctx; + + ctx->emit_type_info = true; + ctx->emit_line_directives = true; + ctx->debugger_break_on_compiler_error = true; + ctx->same_scope_token = {SAME_SCOPE}; + + arena_init(&ctx->perm_push_only, "Perm Push Only"_s); + arena_init(&ctx->scratch_, "Scratch"_s); + arena_init(&ctx->stage_arena_, "Stage Arena"_s); + arena_init(&ctx->token_arena, "Token Arena"_s); + ctx->scratch = &ctx->scratch_; + ctx->stage_arena = &ctx->stage_arena_; + ctx->perm = &ctx->perm_push_only; + ctx->heap = allocator; + ctx->type_map = map_make(ctx->heap, 2048); + ctx->gen = {ctx->perm}; + ctx->helper_builder = {ctx->perm}; + ctx->scope_ids = 1; + bigint_allocator = ctx->perm; + + ctx->interns = intern_table_make(ctx->perm, ctx->heap, 2048); + + /*#import meta +for i in meta.keywords: + print(f'pctx->keyword_{i.lower()} = pctx->intern("{i}"_s);') +print(f'pctx->interns.first_keyword = pctx->keyword_{meta.keywords[0].lower()}.str;') +print(f'pctx->interns.last_keyword = pctx->keyword_{meta.keywords[-1].lower()}.str;') + +for i in meta.interns: + print(f'pctx->intern_{i.lower()} = pctx->intern("{i}"_s);') + +index = 0 +for i in meta.token_simple_expr: + if i[1] != "SPECIAL": + print(f'pctx->op_info_table[{index}] = {{pctx->intern("{i[1]}"_s), "{i[0].upper()}"_s, TK_{i[0]}, {int(i[2]&meta.BINARY_EXPR>0)}, {int(i[2]&meta.UNARY_EXPR>0)}}};') + index += 1 + + */ + pctx->keyword_struct = pctx->intern("struct"_s); + pctx->keyword_union = pctx->intern("union"_s); + pctx->keyword_true = pctx->intern("true"_s); + pctx->keyword_default = pctx->intern("default"_s); + pctx->keyword_continue = pctx->intern("continue"_s); + pctx->keyword_break = pctx->intern("break"_s); + pctx->keyword_false = pctx->intern("false"_s); + pctx->keyword_return = pctx->intern("return"_s); + pctx->keyword_switch = pctx->intern("switch"_s); + pctx->keyword_assert = pctx->intern("Assert"_s); + pctx->keyword_if = pctx->intern("if"_s); + pctx->keyword_elif = pctx->intern("elif"_s); + pctx->keyword_pass = pctx->intern("pass"_s); + pctx->keyword_else = pctx->intern("else"_s); + pctx->keyword_for = pctx->intern("for"_s); + pctx->keyword_enum = pctx->intern("enum"_s); + pctx->keyword_goto = pctx->intern("goto"_s); + pctx->keyword_defer = pctx->intern("defer"_s); + pctx->interns.first_keyword = pctx->keyword_struct.str; + pctx->interns.last_keyword = pctx->keyword_defer.str; + pctx->intern_typeof = pctx->intern("typeof"_s); + pctx->intern_sizeof = pctx->intern("sizeof"_s); + pctx->intern_len = pctx->intern("Len"_s); + pctx->intern_alignof = pctx->intern("alignof"_s); + pctx->intern_foreign = pctx->intern("foreign"_s); + pctx->intern_strict = pctx->intern("strict"_s); + pctx->intern_void = pctx->intern("void"_s); + pctx->intern_flag = pctx->intern("flag"_s); + pctx->intern_it = pctx->intern("it"_s); + pctx->intern_load = pctx->intern("load"_s); + pctx->intern_import = pctx->intern("import"_s); + pctx->intern_link = pctx->intern("link"_s); + pctx->intern_compiler_breakpoint = pctx->intern("compiler_breakpoint"_s); + pctx->op_info_table[0] = {pctx->intern("*"_s), "MUL"_s, TK_Mul, 1, 0}; + pctx->op_info_table[1] = {pctx->intern("/"_s), "DIV"_s, TK_Div, 1, 0}; + pctx->op_info_table[2] = {pctx->intern("%"_s), "MOD"_s, TK_Mod, 1, 0}; + pctx->op_info_table[3] = {pctx->intern("<<"_s), "LEFTSHIFT"_s, TK_LeftShift, 1, 0}; + pctx->op_info_table[4] = {pctx->intern(">>"_s), "RIGHTSHIFT"_s, TK_RightShift, 1, 0}; + pctx->op_info_table[5] = {pctx->intern("+"_s), "ADD"_s, TK_Add, 1, 1}; + pctx->op_info_table[6] = {pctx->intern("-"_s), "SUB"_s, TK_Sub, 1, 1}; + pctx->op_info_table[7] = {pctx->intern("=="_s), "EQUALS"_s, TK_Equals, 1, 0}; + pctx->op_info_table[8] = {pctx->intern("<="_s), "LESSERTHENOREQUAL"_s, TK_LesserThenOrEqual, 1, 0}; + pctx->op_info_table[9] = {pctx->intern(">="_s), "GREATERTHENOREQUAL"_s, TK_GreaterThenOrEqual, 1, 0}; + pctx->op_info_table[10] = {pctx->intern("<"_s), "LESSERTHEN"_s, TK_LesserThen, 1, 0}; + pctx->op_info_table[11] = {pctx->intern(">"_s), "GREATERTHEN"_s, TK_GreaterThen, 1, 0}; + pctx->op_info_table[12] = {pctx->intern("!="_s), "NOTEQUALS"_s, TK_NotEquals, 1, 0}; + pctx->op_info_table[13] = {pctx->intern("&"_s), "BITAND"_s, TK_BitAnd, 1, 0}; + pctx->op_info_table[14] = {pctx->intern("|"_s), "BITOR"_s, TK_BitOr, 1, 0}; + pctx->op_info_table[15] = {pctx->intern("^"_s), "BITXOR"_s, TK_BitXor, 1, 0}; + pctx->op_info_table[16] = {pctx->intern("&&"_s), "AND"_s, TK_And, 1, 0}; + pctx->op_info_table[17] = {pctx->intern("||"_s), "OR"_s, TK_Or, 1, 0}; + pctx->op_info_table[18] = {pctx->intern("~"_s), "NEG"_s, TK_Neg, 0, 1}; + pctx->op_info_table[19] = {pctx->intern("!"_s), "NOT"_s, TK_Not, 0, 1}; + /*END*/ + + // Init types + pctx->type__void = {TYPE_VOID}; + pctx->type__bool = {TYPE_BOOL, sizeof(bool), __alignof(bool)}; + pctx->type__type = {TYPE_TYPE, sizeof(S64), __alignof(S64)}; + + pctx->type__f32 = {TYPE_F32, sizeof(F32), __alignof(F32)}; + pctx->type__f64 = {TYPE_F64, sizeof(F64), __alignof(F64)}; + + pctx->type__s8 = {TYPE_S8, sizeof(S8), __alignof(S8)}; + pctx->type__s16 = {TYPE_S16, sizeof(S16), __alignof(S16)}; + pctx->type__s32 = {TYPE_S32, sizeof(S32), __alignof(S32)}; + pctx->type__s64 = {TYPE_S64, sizeof(S64), __alignof(S64)}; + + pctx->type__u8 = {TYPE_U8, sizeof(U8), __alignof(U8), true}; + pctx->type__u16 = {TYPE_U16, sizeof(U16), __alignof(U16), true}; + pctx->type__u32 = {TYPE_U32, sizeof(U32), __alignof(U32), true}; + pctx->type__u64 = {TYPE_U64, sizeof(U64), __alignof(U64), true}; + + pctx->type__untyped_bool = {TYPE_UNTYPED_BOOL, sizeof(bool), __alignof(bool)}; + pctx->type__untyped_int = {TYPE_UNTYPED_INT, sizeof(S64), __alignof(S64)}; + pctx->type__untyped_string = {TYPE_UNTYPED_STRING, sizeof(String), __alignof(String)}; + pctx->type__untyped_float = {TYPE_UNTYPED_FLOAT, sizeof(double), __alignof(double)}; + + pctx->type__char = {TYPE_CHAR, sizeof(char), __alignof(char)}; + pctx->type__uchar = {TYPE_UCHAR, sizeof(unsigned char), __alignof(unsigned char), true}; + pctx->type__int = {TYPE_INT, sizeof(int), __alignof(int)}; + pctx->type__uint = {TYPE_UINT, sizeof(unsigned), __alignof(unsigned), true}; + pctx->type__long = {TYPE_LONG, sizeof(long), __alignof(long)}; + pctx->type__ulong = {TYPE_ULONG, sizeof(unsigned long), __alignof(unsigned long), true}; + pctx->type__llong = {TYPE_LLONG, sizeof(long long), __alignof(long long)}; + pctx->type__ullong = {TYPE_ULLONG, sizeof(unsigned long long), __alignof(unsigned long long), true}; + pctx->type__short = {TYPE_SHORT, sizeof(short), __alignof(short)}; + pctx->type__ushort = {TYPE_USHORT, sizeof(unsigned short), __alignof(unsigned short), true}; + + pctx->type_char = &pctx->type__char; + pctx->type_uchar = &pctx->type__uchar; + pctx->type_int = &pctx->type__int; + pctx->type_uint = &pctx->type__uint; + pctx->type_long = &pctx->type__long; + pctx->type_ulong = &pctx->type__ulong; + pctx->type_llong = &pctx->type__llong; + pctx->type_ullong = &pctx->type__ullong; + pctx->type_short = &pctx->type__short; + pctx->type_ushort = &pctx->type__ushort; + + pctx->type_void = &pctx->type__void; + + // pctx->type__string = {TYPE_STRING, sizeof(String), __alignof(String)}; + // pctx->type_any; // Needs to be inited at runtime + // pctx->type_string = &pctx->type__string; + + pctx->type_type = &pctx->type__type; + pctx->type_bool = &pctx->type__bool; + + pctx->type_f32 = &pctx->type__f32; + pctx->type_f64 = &pctx->type__f64; + + pctx->type_s8 = &pctx->type__s8; + pctx->type_s16 = &pctx->type__s16; + pctx->type_s32 = &pctx->type__s32; + pctx->type_s64 = &pctx->type__s64; + + pctx->type_u8 = &pctx->type__u8; + pctx->type_u16 = &pctx->type__u16; + pctx->type_u32 = &pctx->type__u32; + pctx->type_u64 = &pctx->type__u64; + + pctx->untyped_string = &pctx->type__untyped_string; + pctx->untyped_bool = &pctx->type__untyped_bool; + pctx->untyped_int = &pctx->type__untyped_int; + pctx->untyped_float = &pctx->type__untyped_float; + + pctx->type__vargs = {TYPE_VARGS}; + pctx->type_vargs = &pctx->type__vargs; + + pctx->type_pointer_to_char = type_pointer(pctx->type_char); + pctx->type_pointer_to_void = type_pointer(pctx->type_void); + + // Init paths + ctx->exe_folder = os_get_exe_dir(ctx->perm); + ctx->working_folder = os_get_working_dir(ctx->perm); + + String main_module = string_fmt(ctx->perm, "%Q/modules", ctx->exe_folder); + add(ctx->perm, &ctx->module_folders, main_module); + ctx->time.init_context = os_time() - ctx->time.init_context; +} + +// apifn +CORE_Static void +core_bootstrap_compiler(Allocator *allocator) { + Core_Ctx *ctx = allocate_struct(allocator, Core_Ctx); + core_init_compiler(ctx, allocator); +} + +CORE_Static void +insert_builtin_type_into_scope(Ast_Scope *p, String name, Ast_Type *type) { + Intern_String string = pctx->intern(name); + Ast_Decl *decl = ast_type(0, string, type); + type->type_id = pctx->type_ids++; + decl->parent_scope = p; + decl->state = DECL_RESOLVED; + insert_into_scope(p, decl); + add(pctx->perm, &pctx->all_types, type); +} + +CORE_Static void +parse_all_modules() { + pctx->time.parsing = os_time(); + + For2(pctx->modules, module) { + if (module->state != MODULE_REGISTERED) + continue; + + For(module->all_loaded_files) { + parse_file(it); + } + + if (module != pctx->language_base_module) { + add(pctx->perm, &module->implicit_imports, (Ast_Scope *)pctx->language_base_module); + } + + module->state = MODULE_PARSED; + } + pctx->time.parsing = os_time() - pctx->time.parsing; +} + +CORE_Static Ast_Module * +add_module(Token *pos, Intern_String filename, B32 command_line_module, bool string_only_module) { + Arena *scratch = pctx->scratch; + Scoped_Arena _scope(scratch); + String absolute_file_path = {}; + String absolute_base_folder = {}; + + if (string_only_module) { + absolute_file_path = filename.s; + absolute_base_folder = filename.s; + } + else { + // + // Find in working directory + // + if (command_line_module) { + if (os_does_file_exist(filename.s)) { + String path = os_get_absolute_path(scratch, filename.s); + string_path_normalize(path); + absolute_file_path = string_copy(scratch, path); + absolute_base_folder = string_chop_last_slash(path); + } + } + + // + // Find in module folder + // + else { + For(pctx->module_folders) { + String path = string_fmt(scratch, "%Q/%Q", it, filename); + if (os_does_file_exist(path)) { + absolute_file_path = path; + absolute_base_folder = string_chop_last_slash(path); + break; + } + } + } + + if (absolute_file_path.len == 0) { + compiler_error(pos, "Couldn't find the module with name %Q", filename); + } + } + + For(pctx->modules) { + if (string_compare(it->absolute_file_path, absolute_file_path)) { + log_trace("Returning registered module: %Q\n", absolute_file_path); + return it; + } + } + + log_trace("Adding module: %Q\n", filename); + Ast_Module *result = ast_new(Ast_Module, AST_MODULE, pos, 0); + result->absolute_file_path = string_copy(pctx->perm, absolute_file_path); + result->absolute_base_folder = string_copy(pctx->perm, absolute_base_folder); + result->debug_name = string_skip_to_last_slash(result->absolute_file_path); + result->module = result; // @warning: self referential + result->file = result; // @warning: self referential + result->parent_scope = 0; + result->scope_id = pctx->scope_ids++; + + register_ast_file(pos, result->absolute_file_path, result, GLOBAL_IMPLICIT_LOAD); + add(pctx->perm, &pctx->modules, result); + return result; +} + +CORE_Static Ast_File * +custom_parse_string(String string) { + Ast_Module *module = pctx->custom_module; + Ast_File *file = get(&module->all_loaded_files, 0); + file->filecontent = string; + parse_file(file); + return file; +} + +CORE_Static void +resolve_everything_in_module(Ast_Module *module) { + if (module->state == MODULE_RESOLVED) + return; + pctx->time.typechecking = os_time(); + For2(module->all_loaded_files, file) { + For2(file->decls, decl) { + if (decl->flags & AST_POLYMORPH) continue; + + resolve_decl(decl); + if (decl->kind == AST_STRUCT || decl->kind == AST_UNION) type_complete(decl->type_val); + } + } + module->state = MODULE_RESOLVED; + pctx->time.typechecking = os_time() - pctx->time.typechecking; +} + +CORE_Static void +init_language_core() { + pctx->custom_module = add_module(0, pctx->intern("Custom.core"_s), false, true); + pctx->custom_module->state = MODULE_RESOLVED; + + Ast_Module *module = add_module(0, pctx->intern("Language.core"_s), false, true); + get(&module->all_loaded_files, 0)->filecontent = + R"( +Any :: struct + data: *void + type: Type + +String :: struct + data: *U8 + len: int + +Two :: struct($A: Type, $B: Type) + a: A + b: B + +Type_Info_Kind :: enum + S64 // FIRST_NUMERIC + S32 + S16 + S8 + INT + CHAR + U64 + U32 + U16 + U8 + F32 + F64 + POINTER + BOOL // LAST_NUMERIC + STRING + VOID + ARRAY + LAMBDA + STRUCT + UNION + ENUM + TYPE + SLICE + TUPLE + +Type_Info_Struct_Member :: struct + name: String + type: Type + offset: int + +Type_Info :: struct + kind: Type_Info_Kind + size: int + align: int + is_unsigned: bool + type: Type + + base_type: Type + array_size: int + struct_members: []Type_Info_Struct_Member + lambda_arguments: []Type_Info + lambda_return: Type + +type_infos_len: int #foreign +type_infos : *Type_Info #foreign + +GetTypeInfo :: (type: Type): *Type_Info + id := type->int + if id >= type_infos_len + return 0 + return type_infos + id + )"_s; + + { + + insert_builtin_type_into_scope(module, "char"_s, pctx->type_char); + insert_builtin_type_into_scope(module, "uchar"_s, pctx->type_uchar); + insert_builtin_type_into_scope(module, "int"_s, pctx->type_int); + insert_builtin_type_into_scope(module, "uint"_s, pctx->type_uint); + insert_builtin_type_into_scope(module, "long"_s, pctx->type_long); + insert_builtin_type_into_scope(module, "ulong"_s, pctx->type_ulong); + insert_builtin_type_into_scope(module, "llong"_s, pctx->type_llong); + insert_builtin_type_into_scope(module, "ullong"_s, pctx->type_ullong); + insert_builtin_type_into_scope(module, "short"_s, pctx->type_short); + insert_builtin_type_into_scope(module, "ushort"_s, pctx->type_ushort); + + insert_builtin_type_into_scope(module, "S64"_s, pctx->type_s64); + insert_builtin_type_into_scope(module, "S32"_s, pctx->type_s32); + insert_builtin_type_into_scope(module, "S16"_s, pctx->type_s16); + insert_builtin_type_into_scope(module, "S8"_s, pctx->type_s8); + + insert_builtin_type_into_scope(module, "U64"_s, pctx->type_u64); + insert_builtin_type_into_scope(module, "U32"_s, pctx->type_u32); + insert_builtin_type_into_scope(module, "U16"_s, pctx->type_u16); + insert_builtin_type_into_scope(module, "U8"_s, pctx->type_u8); + insert_builtin_type_into_scope(module, "F64"_s, pctx->type_f64); + insert_builtin_type_into_scope(module, "F32"_s, pctx->type_f32); + insert_builtin_type_into_scope(module, "void"_s, pctx->type_void); + insert_builtin_type_into_scope(module, "bool"_s, pctx->type_bool); + // insert_builtin_type_into_scope(module, "String"_s, pctx->type_string); + insert_builtin_type_into_scope(module, "Type"_s, pctx->type_type); + } + + { + Ast_File *file = get(&module->all_loaded_files, 0); + Ast_Scope *scope = ast_decl_scope(&pctx->null_token, pctx->perm, file); + scope->parent_scope = file; + scope->module = module; + Ast_Decl *decl = ast_namespace(&pctx->null_token, scope, pctx->intern("Const"_s)); + decl->state = DECL_RESOLVED; + + Value v1 = {}; + v1.type = pctx->untyped_string; + v1.intern_val = pctx->intern(OS_NAME); + Ast_Decl *const_os1 = ast_const(&pctx->null_token, pctx->intern("OSName"_s), v1); + const_os1->state = DECL_RESOLVED; + insert_into_scope(scope, const_os1); + + Value v2 = {}; + v1.type = pctx->untyped_string; + v1.intern_val = pctx->intern(OS_NAME_LOWER); + Ast_Decl *const_os2 = ast_const(&pctx->null_token, pctx->intern("OSNameLower"_s), v2); + const_os2->state = DECL_RESOLVED; + insert_into_scope(scope, const_os2); + + insert_into_scope(module, decl); + } + + pctx->language_base_module = module; + + parse_all_modules(); + resolve_everything_in_module(module); + + // @note: language stuff needs to be declared before type_info data + // so we mark where it ends + pctx->base_language_ordered_decl_len = length(&pctx->ordered_decls); + Ast_Decl *any_decl = search_for_single_decl(module, pctx->intern("Any"_s)); + assert(any_decl->type == pctx->type_type); + pctx->type_any = any_decl->type_val; + + Ast_Decl *string_decl = search_for_single_decl(module, pctx->intern("String"_s)); + assert(string_decl->type == pctx->type_type); + pctx->type_string = string_decl->type_val; +} + +CORE_Static String +compile_file_to_string(Allocator *allocator, String filename) { + F64 total_time = os_time(); + core_bootstrap_compiler(allocator); + pctx->time.total = total_time; + pctx->time.start = total_time; + init_language_core(); + + Ast_Module *module = add_module(0, pctx->intern(filename), true); + parse_all_modules(); + assert(module); + + resolve_everything_in_module(module); + + String result = compile_to_c_code(); + + pctx->time.total = os_time() - pctx->time.total; + return result; +} diff --git a/core_compiler.h b/src/language/core_compiler.h similarity index 96% rename from core_compiler.h rename to src/language/core_compiler.h index 2a69a3a..f216166 100644 --- a/core_compiler.h +++ b/src/language/core_compiler.h @@ -1,234 +1,234 @@ - -struct Lex_Stream { - String stream; - S64 iter; - - U8 *line_begin; - Intern_String file; - S32 line; - S32 inside_brace_paren; - Array indent_stack; // @scratch_allocated -}; - -struct Core_Ctx { - Allocator *heap; - - Arena perm_push_only; - Arena *perm; // Stores: AST, tokens, interns - - Arena *scratch; - Arena scratch_; - Arena stage_arena_; - Arena *stage_arena; - Arena token_arena; - String_Builder helper_builder; - - int errors_occured; - int warnings_occured; - Core_Message *first_message; - Core_Message *last_message; - - // Lexer stuff - Lex_Stream stream; - Array tokens; - Intern_Table interns; - S64 token_iter; - U32 token_debug_ids; - - // Types - List all_types; - S32 type_ids; - int global_decl_ids; - U64 unique_ids; // @Debug - Map type_map; - - Ast_Module *custom_module; - Ast_Module *language_base_module; - - List files; - List modules; - List ordered_decls; - S32 base_language_ordered_decl_len; - - Ast_Scope *currently_parsed_scope; - Ast_File *currently_parsed_file; - U32 scope_ids; - U32 scope_visit_id; - - List module_folders; - String module_folder; - String exe_folder; - String working_folder; - List files_to_link; - - struct { - F64 typechecking; - F64 code_generation; - F64 total; - F64 init_context; - F64 parsing; - - F64 start; - } time; - int lines_lexed; - - bool debugger_break_on_compiler_error; - - // Codegen stage mostly - S64 indent; - String_Builder gen; - - // Codegen stage configurables - bool emit_line_directives; - bool emit_type_info; - bool single_header_library_mode; - String single_header_library_name; - - Token same_scope_token; - Token null_token; - - /*#import meta -for i in meta.keywords: print(f'Intern_String keyword_{i.lower()};') -for i in meta.interns: print(f'Intern_String intern_{i.lower()};') - */ - Intern_String keyword_struct; - Intern_String keyword_union; - Intern_String keyword_true; - Intern_String keyword_default; - Intern_String keyword_continue; - Intern_String keyword_break; - Intern_String keyword_false; - Intern_String keyword_return; - Intern_String keyword_switch; - Intern_String keyword_assert; - Intern_String keyword_if; - Intern_String keyword_elif; - Intern_String keyword_pass; - Intern_String keyword_else; - Intern_String keyword_for; - Intern_String keyword_enum; - Intern_String keyword_goto; - Intern_String keyword_defer; - Intern_String intern_typeof; - Intern_String intern_sizeof; - Intern_String intern_len; - Intern_String intern_alignof; - Intern_String intern_foreign; - Intern_String intern_strict; - Intern_String intern_void; - Intern_String intern_flag; - Intern_String intern_it; - Intern_String intern_load; - Intern_String intern_import; - Intern_String intern_link; - Intern_String intern_compiler_breakpoint; - /*END*/ - - /*#import meta -size = 0 -for i in meta.token_simple_expr: - if i[1] != "SPECIAL": - size += 1 -print(f"Ast_Operator_Info op_info_table[{size}];") - */ - Ast_Operator_Info op_info_table[20]; - /*END*/ - - Ast_Type type__void; - Ast_Type type__string; - Ast_Type type__bool; - Ast_Type type__type; - - Ast_Type type__f32; - Ast_Type type__f64; - - Ast_Type type__s8; - Ast_Type type__s16; - Ast_Type type__s32; - Ast_Type type__s64; - - Ast_Type type__u8; - Ast_Type type__u16; - Ast_Type type__u32; - Ast_Type type__u64; - - Ast_Type type__untyped_bool; - Ast_Type type__untyped_int; - Ast_Type type__untyped_string; - Ast_Type type__untyped_float; - - Ast_Type type__char; - Ast_Type type__uchar; - Ast_Type type__int; - Ast_Type type__uint; - Ast_Type type__long; - Ast_Type type__ulong; - Ast_Type type__llong; - Ast_Type type__ullong; - Ast_Type type__short; - Ast_Type type__ushort; - - Ast_Type *type_char; - Ast_Type *type_uchar; - Ast_Type *type_int; - Ast_Type *type_uint; - Ast_Type *type_long; - Ast_Type *type_ulong; - Ast_Type *type_llong; - Ast_Type *type_ullong; - Ast_Type *type_short; - Ast_Type *type_ushort; - - Ast_Type *type_void; - - Ast_Type *type_pointer_to_char; - Ast_Type *type_pointer_to_void; - Ast_Type *type_any; // Needs to be inited at runtime - - Ast_Type *type_type; - Ast_Type *type_string; - Ast_Type *type_bool; - - Ast_Type *type_f32; - Ast_Type *type_f64; - - Ast_Type *type_s8; - Ast_Type *type_s16; - Ast_Type *type_s32; - Ast_Type *type_s64; - - Ast_Type *type_u8; - Ast_Type *type_u16; - Ast_Type *type_u32; - Ast_Type *type_u64; - - Ast_Type *untyped_string; - Ast_Type *untyped_bool; - Ast_Type *untyped_int; - Ast_Type *untyped_float; - - Ast_Type type__vargs; - Ast_Type *type_vargs; - - Intern_String intern(String string) { - assert(string.len > 0); - return intern_string(&interns, string); - } - - Intern_String internf(char *str, ...) { - STRING_FMT(this->stage_arena, str, result); - return intern_string(&interns, result); - } - - String fmt(char *str, ...) { - STRING_FMT(this->stage_arena, str, result); - return result; - } -}; - -CORE_Static String compile_to_c_code(); -CORE_Static Ast_Module *ast_module(Token *pos, Intern_String filename); -CORE_Static void insert_builtin_types_into_scope(Ast_Scope *p); -CORE_Static void insert_into_scope(Ast_Scope *scope, Ast_Decl *decl); -CORE_Static Ast_Type *type_incomplete(Ast *ast); + +struct Lex_Stream { + String stream; + S64 iter; + + U8 *line_begin; + Intern_String file; + S32 line; + S32 inside_brace_paren; + Array indent_stack; // @scratch_allocated +}; + +struct Core_Ctx { + Allocator *heap; + + Arena perm_push_only; + Arena *perm; // Stores: AST, tokens, interns + + Arena *scratch; + Arena scratch_; + Arena stage_arena_; + Arena *stage_arena; + Arena token_arena; + String_Builder helper_builder; + + int errors_occured; + int warnings_occured; + Core_Message *first_message; + Core_Message *last_message; + + // Lexer stuff + Lex_Stream stream; + Array tokens; + Intern_Table interns; + S64 token_iter; + U32 token_debug_ids; + + // Types + List all_types; + S32 type_ids; + int global_decl_ids; + U64 unique_ids; // @Debug + Map type_map; + + Ast_Module *custom_module; + Ast_Module *language_base_module; + + List files; + List modules; + List ordered_decls; + S32 base_language_ordered_decl_len; + + Ast_Scope *currently_parsed_scope; + Ast_File *currently_parsed_file; + U32 scope_ids; + U32 scope_visit_id; + + List module_folders; + String module_folder; + String exe_folder; + String working_folder; + List files_to_link; + + struct { + F64 typechecking; + F64 code_generation; + F64 total; + F64 init_context; + F64 parsing; + + F64 start; + } time; + int lines_lexed; + + bool debugger_break_on_compiler_error; + + // Codegen stage mostly + S64 indent; + String_Builder gen; + + // Codegen stage configurables + bool emit_line_directives; + bool emit_type_info; + bool single_header_library_mode; + String single_header_library_name; + + Token same_scope_token; + Token null_token; + + /*#import meta +for i in meta.keywords: print(f'Intern_String keyword_{i.lower()};') +for i in meta.interns: print(f'Intern_String intern_{i.lower()};') + */ + Intern_String keyword_struct; + Intern_String keyword_union; + Intern_String keyword_true; + Intern_String keyword_default; + Intern_String keyword_continue; + Intern_String keyword_break; + Intern_String keyword_false; + Intern_String keyword_return; + Intern_String keyword_switch; + Intern_String keyword_assert; + Intern_String keyword_if; + Intern_String keyword_elif; + Intern_String keyword_pass; + Intern_String keyword_else; + Intern_String keyword_for; + Intern_String keyword_enum; + Intern_String keyword_goto; + Intern_String keyword_defer; + Intern_String intern_typeof; + Intern_String intern_sizeof; + Intern_String intern_len; + Intern_String intern_alignof; + Intern_String intern_foreign; + Intern_String intern_strict; + Intern_String intern_void; + Intern_String intern_flag; + Intern_String intern_it; + Intern_String intern_load; + Intern_String intern_import; + Intern_String intern_link; + Intern_String intern_compiler_breakpoint; + /*END*/ + + /*#import meta +size = 0 +for i in meta.token_simple_expr: + if i[1] != "SPECIAL": + size += 1 +print(f"Ast_Operator_Info op_info_table[{size}];") + */ + Ast_Operator_Info op_info_table[20]; + /*END*/ + + Ast_Type type__void; + Ast_Type type__string; + Ast_Type type__bool; + Ast_Type type__type; + + Ast_Type type__f32; + Ast_Type type__f64; + + Ast_Type type__s8; + Ast_Type type__s16; + Ast_Type type__s32; + Ast_Type type__s64; + + Ast_Type type__u8; + Ast_Type type__u16; + Ast_Type type__u32; + Ast_Type type__u64; + + Ast_Type type__untyped_bool; + Ast_Type type__untyped_int; + Ast_Type type__untyped_string; + Ast_Type type__untyped_float; + + Ast_Type type__char; + Ast_Type type__uchar; + Ast_Type type__int; + Ast_Type type__uint; + Ast_Type type__long; + Ast_Type type__ulong; + Ast_Type type__llong; + Ast_Type type__ullong; + Ast_Type type__short; + Ast_Type type__ushort; + + Ast_Type *type_char; + Ast_Type *type_uchar; + Ast_Type *type_int; + Ast_Type *type_uint; + Ast_Type *type_long; + Ast_Type *type_ulong; + Ast_Type *type_llong; + Ast_Type *type_ullong; + Ast_Type *type_short; + Ast_Type *type_ushort; + + Ast_Type *type_void; + + Ast_Type *type_pointer_to_char; + Ast_Type *type_pointer_to_void; + Ast_Type *type_any; // Needs to be inited at runtime + + Ast_Type *type_type; + Ast_Type *type_string; + Ast_Type *type_bool; + + Ast_Type *type_f32; + Ast_Type *type_f64; + + Ast_Type *type_s8; + Ast_Type *type_s16; + Ast_Type *type_s32; + Ast_Type *type_s64; + + Ast_Type *type_u8; + Ast_Type *type_u16; + Ast_Type *type_u32; + Ast_Type *type_u64; + + Ast_Type *untyped_string; + Ast_Type *untyped_bool; + Ast_Type *untyped_int; + Ast_Type *untyped_float; + + Ast_Type type__vargs; + Ast_Type *type_vargs; + + Intern_String intern(String string) { + assert(string.len > 0); + return intern_string(&interns, string); + } + + Intern_String internf(char *str, ...) { + STRING_FMT(this->stage_arena, str, result); + return intern_string(&interns, result); + } + + String fmt(char *str, ...) { + STRING_FMT(this->stage_arena, str, result); + return result; + } +}; + +CORE_Static String compile_to_c_code(); +CORE_Static Ast_Module *ast_module(Token *pos, Intern_String filename); +CORE_Static void insert_builtin_types_into_scope(Ast_Scope *p); +CORE_Static void insert_into_scope(Ast_Scope *scope, Ast_Decl *decl); +CORE_Static Ast_Type *type_incomplete(Ast *ast); CORE_Static Ast_Expr *parse_expr(S64 minbp = 0); \ No newline at end of file diff --git a/core_compiler_includes.cpp b/src/language/core_compiler_includes.cpp similarity index 64% rename from core_compiler_includes.cpp rename to src/language/core_compiler_includes.cpp index 69b0c4a..f3e30f6 100644 --- a/core_compiler_includes.cpp +++ b/src/language/core_compiler_includes.cpp @@ -1,34 +1,34 @@ -#include "base.cpp" -#define STB_SPRINTF_IMPLEMENTATION -#include "stb_sprintf.h" -#include "base_unicode.cpp" -#include "base_arena.cpp" -#include "base_data_structures.cpp" -#include "base_string.cpp" - -#include "os.h" -#if OS_WINDOWS - #include "os_windows.cpp" -#elif OS_LINUX - #include "os_linux.cpp" -#else - #error Couldnt figure out OS using macros -#endif - -#include "core_compiler_interface.hpp" -#include "c3_big_int.h" -#include "core_compiler.h" -#include "core_types.h" -#include "core_globals.cpp" -#include "core_generated.cpp" -#include "c3_big_int.cpp" -#include "core_lexing.cpp" -#include "core_ast.cpp" -#include "core_parsing.cpp" -#include "core_typechecking.h" -#include "core_types.cpp" -#include "core_polymorph.cpp" -#include "core_typechecking.cpp" -#include "core_compiler.cpp" -#include "core_codegen_c_language.cpp" +#include "../base/base.cpp" +#define STB_SPRINTF_IMPLEMENTATION +#include "../base/stb_sprintf.h" +#include "../base/base_unicode.cpp" +#include "../base/base_arena.cpp" +#include "../base/base_data_structures.cpp" +#include "../base/base_string.cpp" + +#include "../os/os.h" +#if OS_WINDOWS + #include "../os/os_windows.cpp" +#elif OS_LINUX + #include "../os/os_linux.cpp" +#else + #error Couldnt figure out OS using macros +#endif + +#include "core_compiler_interface.hpp" +#include "c3_big_int.h" +#include "core_compiler.h" +#include "core_types.h" +#include "core_globals.cpp" +#include "core_generated.cpp" +#include "c3_big_int.cpp" +#include "core_lexing.cpp" +#include "core_ast.cpp" +#include "core_parsing.cpp" +#include "core_typechecking.h" +#include "core_types.cpp" +#include "core_polymorph.cpp" +#include "core_typechecking.cpp" +#include "core_compiler.cpp" +#include "core_codegen_c_language.cpp" #include "core_printer.cpp" \ No newline at end of file diff --git a/core_compiler_interface.hpp b/src/language/core_compiler_interface.hpp similarity index 95% rename from core_compiler_interface.hpp rename to src/language/core_compiler_interface.hpp index 1c4ccc4..22633c8 100644 --- a/core_compiler_interface.hpp +++ b/src/language/core_compiler_interface.hpp @@ -1,646 +1,646 @@ -#pragma once -#include - -struct Ast; -struct Ast_Scope; -struct Ast_Decl; -struct Ast_File_Namespace; -struct Ast_File; -struct Ast_Module; -struct Ast_Lambda; -struct Ast_Type; -struct Ast_Expr; - -enum Token_Kind { - TK_End, - /*# -import meta -for i in meta.token_kinds: - print(" TK_" + i[0] + ",") - */ - TK_Mul, - TK_Div, - TK_Mod, - TK_LeftShift, - TK_RightShift, - TK_FirstMul = TK_Mul, - TK_LastMul = TK_RightShift, - TK_Add, - TK_Sub, - TK_FirstAdd = TK_Add, - TK_LastAdd = TK_Sub, - TK_Equals, - TK_LesserThenOrEqual, - TK_GreaterThenOrEqual, - TK_LesserThen, - TK_GreaterThen, - TK_NotEquals, - TK_FirstCompare = TK_Equals, - TK_LastCompare = TK_NotEquals, - TK_BitAnd, - TK_BitOr, - TK_BitXor, - TK_And, - TK_Or, - TK_FirstLogical = TK_BitAnd, - TK_LastLogical = TK_Or, - TK_Neg, - TK_Not, - TK_Decrement, - TK_Increment, - TK_PostDecrement, - TK_PostIncrement, - TK_Assign, - TK_ColonAssign, - TK_DivAssign, - TK_MulAssign, - TK_ModAssign, - TK_SubAssign, - TK_AddAssign, - TK_AndAssign, - TK_OrAssign, - TK_XorAssign, - TK_LeftShiftAssign, - TK_RightShiftAssign, - TK_FirstAssign = TK_Assign, - TK_LastAssign = TK_RightShiftAssign, - TK_OpenParen, - TK_CloseParen, - TK_OpenBrace, - TK_CloseBrace, - TK_OpenBracket, - TK_CloseBracket, - TK_Comma, - TK_Pound, - TK_Question, - TK_ThreeDots, - TK_Semicolon, - TK_Dot, - TK_TwoDots, - TK_NewLine, - TK_Colon, - TK_DoubleColon, - TK_At, - TK_Arrow, - TK_Polymorph, - TK_ExprSizeof, - TK_DocComment, - TK_Comment, - TK_Identifier, - TK_UnicodeLit, - TK_StringLit, - TK_Error, - TK_Float, - TK_Integer, - TK_Keyword, - /*END*/ - - TK_Pointer = TK_Mul, - TK_Dereference = TK_BitAnd, - - OPEN_SCOPE = 128, - CLOSE_SCOPE, - SAME_SCOPE, -}; - -struct BigInt { - unsigned digit_count; - bool is_negative; - union { - uint64_t digit; - uint64_t *digits; - }; -}; - -struct Token { - Token_Kind kind; - uint32_t di; // debug_id - union { - String string; - struct { - uint8_t *str; - int64_t len; - }; - }; - - union { - uint32_t unicode; - BigInt int_val; - double f64_val; - String error_val; - Intern_String intern_val; - int64_t indent; - }; - - Intern_String file; - int32_t line; - uint8_t *line_begin; -}; - -enum Ast_Type_Kind { - TYPE_NONE, - TYPE_S64, // FIRST_NUMERIC is_int start - TYPE_S32, - TYPE_S16, - TYPE_S8, - - TYPE_INT, - TYPE_UINT, - TYPE_LONG, - TYPE_ULONG, - TYPE_LLONG, - TYPE_ULLONG, - TYPE_SHORT, - TYPE_USHORT, - TYPE_CHAR, - TYPE_UCHAR, - - TYPE_U64, - TYPE_U32, - TYPE_U16, - TYPE_U8, // is_int end - - TYPE_F32, - TYPE_F64, - TYPE_POINTER, - TYPE_BOOL, // LAST_NUMERIC - TYPE_STRING, - TYPE_VOID, - TYPE_ARRAY, - TYPE_LAMBDA, - TYPE_STRUCT, - TYPE_UNION, - TYPE_ENUM, - TYPE_TYPE, - TYPE_SLICE, - - TYPE_COMPLETING, - TYPE_INCOMPLETE, - TYPE_POLYMORPH, - TYPE_VARGS, - - TYPE_UNTYPED_BOOL, // FIRST_TYPED_NUMERIC, FIRST_NUMERIC - TYPE_UNTYPED_INT, - TYPE_UNTYPED_FLOAT, // LAST_TYPED_NUMERIC - TYPE_UNTYPED_STRING, - - TYPE_UNTYPED_FIRST = TYPE_UNTYPED_BOOL, - TYPE_UNTYPED_LAST = TYPE_UNTYPED_STRING, - - TYPE_UNTYPED_FIRST_NUMERIC = TYPE_UNTYPED_BOOL, - TYPE_UNTYPED_LAST_NUMERIC = TYPE_UNTYPED_FLOAT, - - TYPE_FIRST_NUMERIC = TYPE_S64, - TYPE_LAST_NUMERIC = TYPE_BOOL, -}; - -struct Value { - /*#import meta -print(meta.value_struct_content) - */ - Ast_Type *type; - Ast_Decl *resolved_decl; - union { - bool bool_val; - double f64_val; - Intern_String intern_val; - BigInt big_int_val; - Ast_Type *type_val; - }; - /*END*/ -}; - -struct Ast_Resolved_Member { - Intern_String name; - int32_t offset; - bool visited; - /*#import meta -meta.inline_value_fields() - */ - union { - Value value; - struct { - Ast_Type *type; - Ast_Decl *resolved_decl; - union { - bool bool_val; - double f64_val; - Intern_String intern_val; - BigInt big_int_val; - Ast_Type *type_val; - }; - }; - }; - /*END*/ -}; - -struct Ast_Type { - Ast_Type_Kind kind; - int32_t size; - int32_t align; - int32_t is_unsigned; - int32_t type_id; - int32_t padding; - - Ast *ast; - union { - Ast_Type *base; - struct { - Ast_Type *base; - int32_t size; - // @note: if you have array with size "[32]" - // you still want to pass that array into - // a function that expects an array of size "[]" - // so we want also should check this - uint64_t slice_hash; - } arr; - struct { - Array members; - } agg; - struct { - Ast_Type *ret; - Array args; - uint64_t hash_without_ret; - } func; - }; -}; - -enum Ast_Kind : uint32_t { - AST_NONE, - - AST_NAMESPACE, - - AST_MODULE, - AST_FILE, - AST_SCOPE, - AST_VALUE, - AST_IDENT, - AST_INDEX, - AST_UNARY, - AST_BINARY, - AST_CALL_ITEM, - AST_CALL, - - AST_CONSTANT_ASSERT, - AST_RUNTIME_ASSERT, - AST_SIZE_OF, - AST_LENGTH_OF, - AST_ALIGN_OF, - AST_TYPE_OF, - AST_VARGS_LAMBDA_PARAM, - - AST_DEFER, - AST_LABEL, - AST_GOTO, - AST_SWITCH, - AST_SWITCH_CASE, - AST_VAR_UNPACK, - AST_BREAK, - AST_COMPILER_BREAKPOINT_STMT, - AST_CONTINUE, - AST_COMPOUND, - AST_TYPE, - AST_VAR, - AST_CONST, - AST_ARRAY, - AST_FOR, - AST_IF, - AST_IF_NODE, - AST_RETURN, - AST_PASS, - AST_LAMBDA, - AST_LAMBDA_EXPR, - AST_ENUM, - AST_STRUCT, - AST_UNION, -}; - -typedef uint32_t Ast_Flag; -enum { - AST_EXPR = 1 << 1, - AST_STMT = 1 << 2, - AST_STRICT = 1 << 3, - AST_AGGREGATE = 1 << 4, - AST_AGGREGATE_CHILD = 1 << 5, - AST_ATOM = 1 << 7, - AST_FOREIGN = 1 << 8, - AST_DECL = 1 << 9, - AST_GLOBAL = 1 << 10, - AST_FLAG = 1 << 11, - AST_VAR_IS_CONST = 1 << 12, - AST_OPERATOR_OVERLOAD = 1 << 13, - AST_IS_LVALUE = 1 << 14, - - AST_IDENT_POLYMORPH = 1 << 15, - AST_TYPE_POLYMORPH = 1 << 16, - AST_POLYMORPH = AST_IDENT_POLYMORPH | AST_TYPE_POLYMORPH, - AST_PARENT_POLYMORPH = 1 << 17, - AST_POLYMORPH_INSTANCE = 1 << 18, - - AST_TYPESPEC = 1 << 19, - AST_COMPILER_BREAKPOINT = 1 << 20, -}; - -struct Ast { - uint64_t di; // Debug id, shouldn't ever be used in the program - Token *pos; - - Ast_Kind kind; - uint32_t visit_id; - - Ast_Scope *parent_scope; - Ast_Flag flags; -}; - -struct Ast_Expr : Ast { - Ast_Type *resolved_type; - Ast_Decl *resolved_operator_overload; - union { - Ast_Type *index_original_type; - Ast_Type *cast_after_type; - Ast_Type *dot_access_step_resolution; - }; -}; - -struct Ast_Atom : Ast_Expr { - // We have a field type here - // it has a different purpose from the - // resolved_type of Ast_Expr, it describes - // the inherent type of a value - // - // resolved_type is a solid type that - // can be use during code generation - // it cannot be untyped. (or at least thats the hope :) - /*#import meta -meta.inline_value_fields() - */ - union { - Value value; - struct { - Ast_Type *type; - Ast_Decl *resolved_decl; - union { - bool bool_val; - double f64_val; - Intern_String intern_val; - BigInt big_int_val; - Ast_Type *type_val; - }; - }; - }; - /*END*/ -}; - -typedef uint32_t Ast_Call_Item_Flag; -enum { - CALL_INDEX = 1ull << 1, - CALL_NAME = 1ull << 2, -}; - -struct Ast_Call_Item : Ast_Expr { - Ast_Call_Item_Flag call_flags; - int32_t resolved_index; // This is probably for compound array - Ast_Expr *item; - union { - Ast_Atom *name; - Ast_Expr *index; - }; - Intern_String resolved_name; // This is probably for compound struct -}; - -struct Ast_Call : Ast_Expr { - union { - Ast_Expr *name; - Ast_Expr *typespec; - }; - Array exprs; - Ast_Decl *resolved_decl; -}; - -struct Ast_Var_Unpack : Ast_Expr { - Array vars; - Ast_Expr *expr; -}; - -struct Ast_Unary : Ast_Expr { - Token_Kind op; - Ast_Expr *expr; - uint64_t padding[2]; // For folding constants into atoms -}; - -struct Ast_Index : Ast_Expr { - Ast_Expr *expr; - Ast_Expr *index; -}; - -struct Ast_Binary : Ast_Expr { - Token_Kind op; - Ast_Expr *left; - Ast_Expr *right; - Ast_Type *before_type; -}; - -struct Ast_Builtin : Ast_Expr { - Ast_Expr *expr; - Intern_String assert_message; - uint64_t padding[1]; // For folding constants into atoms -}; - -// Problem: We are parsing out of order, in the middle of parsing a function -// we can jump down a different function, we cant therfore use global map. -// Because it would have symbols from previous function we were in middle of resolving. -// -// On top of that in the future we want a way to inject scopes, for convenience. -// Each scope needs to have it's checked locals list. To lookup syms we need to -// look into global scope and to the locals list. -// -// -// This seems slow though, would be nice to have a simpler scheme that's more flat. -// Would be nice to just reuse single map while resolving that would keep track of which -// function we are resolving. -// -// Also would be nice to have a more flat module scheme. The Ion approach seemed -// very straight forward when I looked at it. It used a single locals list with -// an index that signified from where we should consider declarations. Not really -// sure how the packages worked though. -// -// The idea that you have a flat list of packages, each package has a flat list of declarations. -// Seems nice. -// - -struct Ast_Return : Ast { - Ast_Type *resolved_type; - Ast_Expr *expr; -}; - -struct Ast_If_Node : Ast { - Ast_Expr *expr; - Ast_Scope *scope; - Ast_Binary *init; -}; - -struct Ast_If : Ast { - Array ifs; -}; - -// @todo: Ast_Simple_Stmt -#define Ast_Pass Ast -#define Ast_Break Ast - -struct Ast_Goto : Ast { - Intern_String label; -}; - -struct Ast_Defer : Ast { - Ast_Scope *scope; -}; - -struct Ast_For : Ast { - Ast_Expr *init; - Ast_Expr *cond; - Ast_Expr *iter; - Ast_Scope *scope; - - Ast_Decl *array_traversal_var; - bool is_array_traversal; - bool is_also_slice_traversal; -}; - -struct Ast_Lambda : Ast_Expr { - Array args; - Ast_Expr *ret; - Ast_Scope *scope; -}; - -struct Ast_Array : Ast_Expr { - Ast_Expr *base; - Ast_Expr *expr; - uint64_t padding[2]; -}; - -struct Ast_Switch_Case : Ast { - Array labels; - Ast_Scope *scope; - bool fallthrough; -}; - -struct Ast_Switch : Ast { - Ast_Expr *value; - Array cases; - Ast_Scope *default_scope; -}; - -struct Ast_Scope : Ast { - String debug_name; // Only for debug purposes, dont depend on it - Intern_String first_namespace_name; - - List implicit_imports; - List decls; - Array stmts; - Array defers; - - uint32_t scope_id; - Ast_Scope *file; // Self referential for file and module - Ast_Module *module; - union { - Ast *parent_ast; // @language_todo: add parent decl - Ast_Decl *parent_decl; - }; -}; - -enum Ast_Module_State { - MODULE_REGISTERED, - MODULE_PARSED, - MODULE_RESOLVED, -}; - -struct Ast_Module : Ast_Scope { - Ast_Module_State state; - String absolute_base_folder; - String absolute_file_path; - List all_loaded_files; -}; - -struct Ast_File : Ast_Scope { - String absolute_base_folder; - String absolute_file_path; - String filecontent; -}; - -enum Ast_Decl_State { - DECL_NOT_RESOLVED, - DECL_RESOLVED, - DECL_RESOLVED_TYPE, - DECL_RESOLVING, -}; - -struct Ast_Operator_Info { - Intern_String op; - String name; - Token_Kind op_kind; - bool valid_binary_expr; - bool valid_unary_expr; -}; - -struct Ast_Decl : Ast { - Ast_Decl_State state; - Intern_String name; - Intern_String unique_name; // For code generation, currently only present on lambdas - - // @todo: Move this to Ast_Operator_Overload - uint64_t operator_overload_arguments_hash; - Ast_Operator_Info *overload_op_info; - - // @todo: move this to Ast_Poly, perhaps this change needs to be combined with - // the change to lambda where they stop being expressions - uint64_t polymorph_hash; - Array polymorph_resolved_parameter_types; - Array polymorph_parameters; - Array polymorphs; // instantiated polymorphs - - Ast_Scope *scope; - Ast_Expr *typespec; - union { - Ast_Expr *expr; - Ast_Lambda *lambda; - }; - - /*#import meta -meta.inline_value_fields() - */ - union { - Value value; - struct { - Ast_Type *type; - Ast_Decl *resolved_decl; - union { - bool bool_val; - double f64_val; - Intern_String intern_val; - BigInt big_int_val; - Ast_Type *type_val; - }; - }; - }; - /*END*/ -}; - -struct Ast_Label : Ast_Decl { - bool enable_goto; -}; - -enum Core_Message_Kind { - CORE_ERROR, - CORE_WARNING, - CORE_TRACE, -}; - -struct Core_Message { - Core_Message *next; - Core_Message_Kind kind; - String string; - Token *tokens[2]; - - int trace_line; - char *trace_file; -}; +#pragma once +#include + +struct Ast; +struct Ast_Scope; +struct Ast_Decl; +struct Ast_File_Namespace; +struct Ast_File; +struct Ast_Module; +struct Ast_Lambda; +struct Ast_Type; +struct Ast_Expr; + +enum Token_Kind { + TK_End, + /*# +import meta +for i in meta.token_kinds: + print(" TK_" + i[0] + ",") + */ + TK_Mul, + TK_Div, + TK_Mod, + TK_LeftShift, + TK_RightShift, + TK_FirstMul = TK_Mul, + TK_LastMul = TK_RightShift, + TK_Add, + TK_Sub, + TK_FirstAdd = TK_Add, + TK_LastAdd = TK_Sub, + TK_Equals, + TK_LesserThenOrEqual, + TK_GreaterThenOrEqual, + TK_LesserThen, + TK_GreaterThen, + TK_NotEquals, + TK_FirstCompare = TK_Equals, + TK_LastCompare = TK_NotEquals, + TK_BitAnd, + TK_BitOr, + TK_BitXor, + TK_And, + TK_Or, + TK_FirstLogical = TK_BitAnd, + TK_LastLogical = TK_Or, + TK_Neg, + TK_Not, + TK_Decrement, + TK_Increment, + TK_PostDecrement, + TK_PostIncrement, + TK_Assign, + TK_ColonAssign, + TK_DivAssign, + TK_MulAssign, + TK_ModAssign, + TK_SubAssign, + TK_AddAssign, + TK_AndAssign, + TK_OrAssign, + TK_XorAssign, + TK_LeftShiftAssign, + TK_RightShiftAssign, + TK_FirstAssign = TK_Assign, + TK_LastAssign = TK_RightShiftAssign, + TK_OpenParen, + TK_CloseParen, + TK_OpenBrace, + TK_CloseBrace, + TK_OpenBracket, + TK_CloseBracket, + TK_Comma, + TK_Pound, + TK_Question, + TK_ThreeDots, + TK_Semicolon, + TK_Dot, + TK_TwoDots, + TK_NewLine, + TK_Colon, + TK_DoubleColon, + TK_At, + TK_Arrow, + TK_Polymorph, + TK_ExprSizeof, + TK_DocComment, + TK_Comment, + TK_Identifier, + TK_UnicodeLit, + TK_StringLit, + TK_Error, + TK_Float, + TK_Integer, + TK_Keyword, + /*END*/ + + TK_Pointer = TK_Mul, + TK_Dereference = TK_BitAnd, + + OPEN_SCOPE = 128, + CLOSE_SCOPE, + SAME_SCOPE, +}; + +struct BigInt { + unsigned digit_count; + bool is_negative; + union { + uint64_t digit; + uint64_t *digits; + }; +}; + +struct Token { + Token_Kind kind; + uint32_t di; // debug_id + union { + String string; + struct { + uint8_t *str; + int64_t len; + }; + }; + + union { + uint32_t unicode; + BigInt int_val; + double f64_val; + String error_val; + Intern_String intern_val; + int64_t indent; + }; + + Intern_String file; + int32_t line; + uint8_t *line_begin; +}; + +enum Ast_Type_Kind { + TYPE_NONE, + TYPE_S64, // FIRST_NUMERIC is_int start + TYPE_S32, + TYPE_S16, + TYPE_S8, + + TYPE_INT, + TYPE_UINT, + TYPE_LONG, + TYPE_ULONG, + TYPE_LLONG, + TYPE_ULLONG, + TYPE_SHORT, + TYPE_USHORT, + TYPE_CHAR, + TYPE_UCHAR, + + TYPE_U64, + TYPE_U32, + TYPE_U16, + TYPE_U8, // is_int end + + TYPE_F32, + TYPE_F64, + TYPE_POINTER, + TYPE_BOOL, // LAST_NUMERIC + TYPE_STRING, + TYPE_VOID, + TYPE_ARRAY, + TYPE_LAMBDA, + TYPE_STRUCT, + TYPE_UNION, + TYPE_ENUM, + TYPE_TYPE, + TYPE_SLICE, + + TYPE_COMPLETING, + TYPE_INCOMPLETE, + TYPE_POLYMORPH, + TYPE_VARGS, + + TYPE_UNTYPED_BOOL, // FIRST_TYPED_NUMERIC, FIRST_NUMERIC + TYPE_UNTYPED_INT, + TYPE_UNTYPED_FLOAT, // LAST_TYPED_NUMERIC + TYPE_UNTYPED_STRING, + + TYPE_UNTYPED_FIRST = TYPE_UNTYPED_BOOL, + TYPE_UNTYPED_LAST = TYPE_UNTYPED_STRING, + + TYPE_UNTYPED_FIRST_NUMERIC = TYPE_UNTYPED_BOOL, + TYPE_UNTYPED_LAST_NUMERIC = TYPE_UNTYPED_FLOAT, + + TYPE_FIRST_NUMERIC = TYPE_S64, + TYPE_LAST_NUMERIC = TYPE_BOOL, +}; + +struct Value { + /*#import meta +print(meta.value_struct_content) + */ + Ast_Type *type; + Ast_Decl *resolved_decl; + union { + bool bool_val; + double f64_val; + Intern_String intern_val; + BigInt big_int_val; + Ast_Type *type_val; + }; + /*END*/ +}; + +struct Ast_Resolved_Member { + Intern_String name; + int32_t offset; + bool visited; + /*#import meta +meta.inline_value_fields() + */ + union { + Value value; + struct { + Ast_Type *type; + Ast_Decl *resolved_decl; + union { + bool bool_val; + double f64_val; + Intern_String intern_val; + BigInt big_int_val; + Ast_Type *type_val; + }; + }; + }; + /*END*/ +}; + +struct Ast_Type { + Ast_Type_Kind kind; + int32_t size; + int32_t align; + int32_t is_unsigned; + int32_t type_id; + int32_t padding; + + Ast *ast; + union { + Ast_Type *base; + struct { + Ast_Type *base; + int32_t size; + // @note: if you have array with size "[32]" + // you still want to pass that array into + // a function that expects an array of size "[]" + // so we want also should check this + uint64_t slice_hash; + } arr; + struct { + Array members; + } agg; + struct { + Ast_Type *ret; + Array args; + uint64_t hash_without_ret; + } func; + }; +}; + +enum Ast_Kind : uint32_t { + AST_NONE, + + AST_NAMESPACE, + + AST_MODULE, + AST_FILE, + AST_SCOPE, + AST_VALUE, + AST_IDENT, + AST_INDEX, + AST_UNARY, + AST_BINARY, + AST_CALL_ITEM, + AST_CALL, + + AST_CONSTANT_ASSERT, + AST_RUNTIME_ASSERT, + AST_SIZE_OF, + AST_LENGTH_OF, + AST_ALIGN_OF, + AST_TYPE_OF, + AST_VARGS_LAMBDA_PARAM, + + AST_DEFER, + AST_LABEL, + AST_GOTO, + AST_SWITCH, + AST_SWITCH_CASE, + AST_VAR_UNPACK, + AST_BREAK, + AST_COMPILER_BREAKPOINT_STMT, + AST_CONTINUE, + AST_COMPOUND, + AST_TYPE, + AST_VAR, + AST_CONST, + AST_ARRAY, + AST_FOR, + AST_IF, + AST_IF_NODE, + AST_RETURN, + AST_PASS, + AST_LAMBDA, + AST_LAMBDA_EXPR, + AST_ENUM, + AST_STRUCT, + AST_UNION, +}; + +typedef uint32_t Ast_Flag; +enum { + AST_EXPR = 1 << 1, + AST_STMT = 1 << 2, + AST_STRICT = 1 << 3, + AST_AGGREGATE = 1 << 4, + AST_AGGREGATE_CHILD = 1 << 5, + AST_ATOM = 1 << 7, + AST_FOREIGN = 1 << 8, + AST_DECL = 1 << 9, + AST_GLOBAL = 1 << 10, + AST_FLAG = 1 << 11, + AST_VAR_IS_CONST = 1 << 12, + AST_OPERATOR_OVERLOAD = 1 << 13, + AST_IS_LVALUE = 1 << 14, + + AST_IDENT_POLYMORPH = 1 << 15, + AST_TYPE_POLYMORPH = 1 << 16, + AST_POLYMORPH = AST_IDENT_POLYMORPH | AST_TYPE_POLYMORPH, + AST_PARENT_POLYMORPH = 1 << 17, + AST_POLYMORPH_INSTANCE = 1 << 18, + + AST_TYPESPEC = 1 << 19, + AST_COMPILER_BREAKPOINT = 1 << 20, +}; + +struct Ast { + uint64_t di; // Debug id, shouldn't ever be used in the program + Token *pos; + + Ast_Kind kind; + uint32_t visit_id; + + Ast_Scope *parent_scope; + Ast_Flag flags; +}; + +struct Ast_Expr : Ast { + Ast_Type *resolved_type; + Ast_Decl *resolved_operator_overload; + union { + Ast_Type *index_original_type; + Ast_Type *cast_after_type; + Ast_Type *dot_access_step_resolution; + }; +}; + +struct Ast_Atom : Ast_Expr { + // We have a field type here + // it has a different purpose from the + // resolved_type of Ast_Expr, it describes + // the inherent type of a value + // + // resolved_type is a solid type that + // can be use during code generation + // it cannot be untyped. (or at least thats the hope :) + /*#import meta +meta.inline_value_fields() + */ + union { + Value value; + struct { + Ast_Type *type; + Ast_Decl *resolved_decl; + union { + bool bool_val; + double f64_val; + Intern_String intern_val; + BigInt big_int_val; + Ast_Type *type_val; + }; + }; + }; + /*END*/ +}; + +typedef uint32_t Ast_Call_Item_Flag; +enum { + CALL_INDEX = 1ull << 1, + CALL_NAME = 1ull << 2, +}; + +struct Ast_Call_Item : Ast_Expr { + Ast_Call_Item_Flag call_flags; + int32_t resolved_index; // This is probably for compound array + Ast_Expr *item; + union { + Ast_Atom *name; + Ast_Expr *index; + }; + Intern_String resolved_name; // This is probably for compound struct +}; + +struct Ast_Call : Ast_Expr { + union { + Ast_Expr *name; + Ast_Expr *typespec; + }; + Array exprs; + Ast_Decl *resolved_decl; +}; + +struct Ast_Var_Unpack : Ast_Expr { + Array vars; + Ast_Expr *expr; +}; + +struct Ast_Unary : Ast_Expr { + Token_Kind op; + Ast_Expr *expr; + uint64_t padding[2]; // For folding constants into atoms +}; + +struct Ast_Index : Ast_Expr { + Ast_Expr *expr; + Ast_Expr *index; +}; + +struct Ast_Binary : Ast_Expr { + Token_Kind op; + Ast_Expr *left; + Ast_Expr *right; + Ast_Type *before_type; +}; + +struct Ast_Builtin : Ast_Expr { + Ast_Expr *expr; + Intern_String assert_message; + uint64_t padding[1]; // For folding constants into atoms +}; + +// Problem: We are parsing out of order, in the middle of parsing a function +// we can jump down a different function, we cant therfore use global map. +// Because it would have symbols from previous function we were in middle of resolving. +// +// On top of that in the future we want a way to inject scopes, for convenience. +// Each scope needs to have it's checked locals list. To lookup syms we need to +// look into global scope and to the locals list. +// +// +// This seems slow though, would be nice to have a simpler scheme that's more flat. +// Would be nice to just reuse single map while resolving that would keep track of which +// function we are resolving. +// +// Also would be nice to have a more flat module scheme. The Ion approach seemed +// very straight forward when I looked at it. It used a single locals list with +// an index that signified from where we should consider declarations. Not really +// sure how the packages worked though. +// +// The idea that you have a flat list of packages, each package has a flat list of declarations. +// Seems nice. +// + +struct Ast_Return : Ast { + Ast_Type *resolved_type; + Ast_Expr *expr; +}; + +struct Ast_If_Node : Ast { + Ast_Expr *expr; + Ast_Scope *scope; + Ast_Binary *init; +}; + +struct Ast_If : Ast { + Array ifs; +}; + +// @todo: Ast_Simple_Stmt +#define Ast_Pass Ast +#define Ast_Break Ast + +struct Ast_Goto : Ast { + Intern_String label; +}; + +struct Ast_Defer : Ast { + Ast_Scope *scope; +}; + +struct Ast_For : Ast { + Ast_Expr *init; + Ast_Expr *cond; + Ast_Expr *iter; + Ast_Scope *scope; + + Ast_Decl *array_traversal_var; + bool is_array_traversal; + bool is_also_slice_traversal; +}; + +struct Ast_Lambda : Ast_Expr { + Array args; + Ast_Expr *ret; + Ast_Scope *scope; +}; + +struct Ast_Array : Ast_Expr { + Ast_Expr *base; + Ast_Expr *expr; + uint64_t padding[2]; +}; + +struct Ast_Switch_Case : Ast { + Array labels; + Ast_Scope *scope; + bool fallthrough; +}; + +struct Ast_Switch : Ast { + Ast_Expr *value; + Array cases; + Ast_Scope *default_scope; +}; + +struct Ast_Scope : Ast { + String debug_name; // Only for debug purposes, dont depend on it + Intern_String first_namespace_name; + + List implicit_imports; + List decls; + Array stmts; + Array defers; + + uint32_t scope_id; + Ast_Scope *file; // Self referential for file and module + Ast_Module *module; + union { + Ast *parent_ast; // @language_todo: add parent decl + Ast_Decl *parent_decl; + }; +}; + +enum Ast_Module_State { + MODULE_REGISTERED, + MODULE_PARSED, + MODULE_RESOLVED, +}; + +struct Ast_Module : Ast_Scope { + Ast_Module_State state; + String absolute_base_folder; + String absolute_file_path; + List all_loaded_files; +}; + +struct Ast_File : Ast_Scope { + String absolute_base_folder; + String absolute_file_path; + String filecontent; +}; + +enum Ast_Decl_State { + DECL_NOT_RESOLVED, + DECL_RESOLVED, + DECL_RESOLVED_TYPE, + DECL_RESOLVING, +}; + +struct Ast_Operator_Info { + Intern_String op; + String name; + Token_Kind op_kind; + bool valid_binary_expr; + bool valid_unary_expr; +}; + +struct Ast_Decl : Ast { + Ast_Decl_State state; + Intern_String name; + Intern_String unique_name; // For code generation, currently only present on lambdas + + // @todo: Move this to Ast_Operator_Overload + uint64_t operator_overload_arguments_hash; + Ast_Operator_Info *overload_op_info; + + // @todo: move this to Ast_Poly, perhaps this change needs to be combined with + // the change to lambda where they stop being expressions + uint64_t polymorph_hash; + Array polymorph_resolved_parameter_types; + Array polymorph_parameters; + Array polymorphs; // instantiated polymorphs + + Ast_Scope *scope; + Ast_Expr *typespec; + union { + Ast_Expr *expr; + Ast_Lambda *lambda; + }; + + /*#import meta +meta.inline_value_fields() + */ + union { + Value value; + struct { + Ast_Type *type; + Ast_Decl *resolved_decl; + union { + bool bool_val; + double f64_val; + Intern_String intern_val; + BigInt big_int_val; + Ast_Type *type_val; + }; + }; + }; + /*END*/ +}; + +struct Ast_Label : Ast_Decl { + bool enable_goto; +}; + +enum Core_Message_Kind { + CORE_ERROR, + CORE_WARNING, + CORE_TRACE, +}; + +struct Core_Message { + Core_Message *next; + Core_Message_Kind kind; + String string; + Token *tokens[2]; + + int trace_line; + char *trace_file; +}; diff --git a/core_generated.cpp b/src/language/core_generated.cpp similarity index 97% rename from core_generated.cpp rename to src/language/core_generated.cpp index f4684c2..4ef96da 100644 --- a/core_generated.cpp +++ b/src/language/core_generated.cpp @@ -1,82 +1,82 @@ - -/*#import meta -print("""CORE_Static Ast_Operator_Info * -get_operator_info(Token_Kind op){ - switch(op){""") - -index = 0 -for i in meta.token_simple_expr: - if i[1] != "SPECIAL": - print(f""" case TK_{i[0]}: return pctx->op_info_table + {index};""") - index += 1 -print(" default: {}\n }") -print(" return 0;\n}") - -print("""CORE_Static Ast_Operator_Info * -get_operator_info(Intern_String op){ - if(0){}""") - -index = 0 -for i in meta.token_simple_expr: - if i[1] != "SPECIAL": - print(f""" else if(pctx->op_info_table[{index}].op.str == op.str) return pctx->op_info_table + {index};""") - index += 1 -print(" return 0;\n}") - - -*/ -CORE_Static Ast_Operator_Info * -get_operator_info(Token_Kind op) { - switch (op) { - case TK_Mul: return pctx->op_info_table + 0; - case TK_Div: return pctx->op_info_table + 1; - case TK_Mod: return pctx->op_info_table + 2; - case TK_LeftShift: return pctx->op_info_table + 3; - case TK_RightShift: return pctx->op_info_table + 4; - case TK_Add: return pctx->op_info_table + 5; - case TK_Sub: return pctx->op_info_table + 6; - case TK_Equals: return pctx->op_info_table + 7; - case TK_LesserThenOrEqual: return pctx->op_info_table + 8; - case TK_GreaterThenOrEqual: return pctx->op_info_table + 9; - case TK_LesserThen: return pctx->op_info_table + 10; - case TK_GreaterThen: return pctx->op_info_table + 11; - case TK_NotEquals: return pctx->op_info_table + 12; - case TK_BitAnd: return pctx->op_info_table + 13; - case TK_BitOr: return pctx->op_info_table + 14; - case TK_BitXor: return pctx->op_info_table + 15; - case TK_And: return pctx->op_info_table + 16; - case TK_Or: return pctx->op_info_table + 17; - case TK_Neg: return pctx->op_info_table + 18; - case TK_Not: return pctx->op_info_table + 19; - default: { - } - } - return 0; -} -CORE_Static Ast_Operator_Info * -get_operator_info(Intern_String op) { - if (0) { - } - else if (pctx->op_info_table[0].op.str == op.str) return pctx->op_info_table + 0; - else if (pctx->op_info_table[1].op.str == op.str) return pctx->op_info_table + 1; - else if (pctx->op_info_table[2].op.str == op.str) return pctx->op_info_table + 2; - else if (pctx->op_info_table[3].op.str == op.str) return pctx->op_info_table + 3; - else if (pctx->op_info_table[4].op.str == op.str) return pctx->op_info_table + 4; - else if (pctx->op_info_table[5].op.str == op.str) return pctx->op_info_table + 5; - else if (pctx->op_info_table[6].op.str == op.str) return pctx->op_info_table + 6; - else if (pctx->op_info_table[7].op.str == op.str) return pctx->op_info_table + 7; - else if (pctx->op_info_table[8].op.str == op.str) return pctx->op_info_table + 8; - else if (pctx->op_info_table[9].op.str == op.str) return pctx->op_info_table + 9; - else if (pctx->op_info_table[10].op.str == op.str) return pctx->op_info_table + 10; - else if (pctx->op_info_table[11].op.str == op.str) return pctx->op_info_table + 11; - else if (pctx->op_info_table[12].op.str == op.str) return pctx->op_info_table + 12; - else if (pctx->op_info_table[13].op.str == op.str) return pctx->op_info_table + 13; - else if (pctx->op_info_table[14].op.str == op.str) return pctx->op_info_table + 14; - else if (pctx->op_info_table[15].op.str == op.str) return pctx->op_info_table + 15; - else if (pctx->op_info_table[16].op.str == op.str) return pctx->op_info_table + 16; - else if (pctx->op_info_table[17].op.str == op.str) return pctx->op_info_table + 17; - else if (pctx->op_info_table[18].op.str == op.str) return pctx->op_info_table + 18; - else if (pctx->op_info_table[19].op.str == op.str) return pctx->op_info_table + 19; - return 0; -} -/*END*/ + +/*#import meta +print("""CORE_Static Ast_Operator_Info * +get_operator_info(Token_Kind op){ + switch(op){""") + +index = 0 +for i in meta.token_simple_expr: + if i[1] != "SPECIAL": + print(f""" case TK_{i[0]}: return pctx->op_info_table + {index};""") + index += 1 +print(" default: {}\n }") +print(" return 0;\n}") + +print("""CORE_Static Ast_Operator_Info * +get_operator_info(Intern_String op){ + if(0){}""") + +index = 0 +for i in meta.token_simple_expr: + if i[1] != "SPECIAL": + print(f""" else if(pctx->op_info_table[{index}].op.str == op.str) return pctx->op_info_table + {index};""") + index += 1 +print(" return 0;\n}") + + +*/ +CORE_Static Ast_Operator_Info * +get_operator_info(Token_Kind op) { + switch (op) { + case TK_Mul: return pctx->op_info_table + 0; + case TK_Div: return pctx->op_info_table + 1; + case TK_Mod: return pctx->op_info_table + 2; + case TK_LeftShift: return pctx->op_info_table + 3; + case TK_RightShift: return pctx->op_info_table + 4; + case TK_Add: return pctx->op_info_table + 5; + case TK_Sub: return pctx->op_info_table + 6; + case TK_Equals: return pctx->op_info_table + 7; + case TK_LesserThenOrEqual: return pctx->op_info_table + 8; + case TK_GreaterThenOrEqual: return pctx->op_info_table + 9; + case TK_LesserThen: return pctx->op_info_table + 10; + case TK_GreaterThen: return pctx->op_info_table + 11; + case TK_NotEquals: return pctx->op_info_table + 12; + case TK_BitAnd: return pctx->op_info_table + 13; + case TK_BitOr: return pctx->op_info_table + 14; + case TK_BitXor: return pctx->op_info_table + 15; + case TK_And: return pctx->op_info_table + 16; + case TK_Or: return pctx->op_info_table + 17; + case TK_Neg: return pctx->op_info_table + 18; + case TK_Not: return pctx->op_info_table + 19; + default: { + } + } + return 0; +} +CORE_Static Ast_Operator_Info * +get_operator_info(Intern_String op) { + if (0) { + } + else if (pctx->op_info_table[0].op.str == op.str) return pctx->op_info_table + 0; + else if (pctx->op_info_table[1].op.str == op.str) return pctx->op_info_table + 1; + else if (pctx->op_info_table[2].op.str == op.str) return pctx->op_info_table + 2; + else if (pctx->op_info_table[3].op.str == op.str) return pctx->op_info_table + 3; + else if (pctx->op_info_table[4].op.str == op.str) return pctx->op_info_table + 4; + else if (pctx->op_info_table[5].op.str == op.str) return pctx->op_info_table + 5; + else if (pctx->op_info_table[6].op.str == op.str) return pctx->op_info_table + 6; + else if (pctx->op_info_table[7].op.str == op.str) return pctx->op_info_table + 7; + else if (pctx->op_info_table[8].op.str == op.str) return pctx->op_info_table + 8; + else if (pctx->op_info_table[9].op.str == op.str) return pctx->op_info_table + 9; + else if (pctx->op_info_table[10].op.str == op.str) return pctx->op_info_table + 10; + else if (pctx->op_info_table[11].op.str == op.str) return pctx->op_info_table + 11; + else if (pctx->op_info_table[12].op.str == op.str) return pctx->op_info_table + 12; + else if (pctx->op_info_table[13].op.str == op.str) return pctx->op_info_table + 13; + else if (pctx->op_info_table[14].op.str == op.str) return pctx->op_info_table + 14; + else if (pctx->op_info_table[15].op.str == op.str) return pctx->op_info_table + 15; + else if (pctx->op_info_table[16].op.str == op.str) return pctx->op_info_table + 16; + else if (pctx->op_info_table[17].op.str == op.str) return pctx->op_info_table + 17; + else if (pctx->op_info_table[18].op.str == op.str) return pctx->op_info_table + 18; + else if (pctx->op_info_table[19].op.str == op.str) return pctx->op_info_table + 19; + return 0; +} +/*END*/ diff --git a/core_globals.cpp b/src/language/core_globals.cpp similarity index 96% rename from core_globals.cpp rename to src/language/core_globals.cpp index 05c69a7..acf4f07 100644 --- a/core_globals.cpp +++ b/src/language/core_globals.cpp @@ -1,7 +1,7 @@ -thread_local Core_Ctx *pctx; - -Allocator *bigint_allocator; -global S64 bigint_allocation_count; - -const uintptr_t pointer_size = sizeof(uintptr_t); -const uintptr_t pointer_align = __alignof(uintptr_t); +thread_local Core_Ctx *pctx; + +Allocator *bigint_allocator; +global S64 bigint_allocation_count; + +const uintptr_t pointer_size = sizeof(uintptr_t); +const uintptr_t pointer_align = __alignof(uintptr_t); diff --git a/core_lexing.cpp b/src/language/core_lexing.cpp similarity index 96% rename from core_lexing.cpp rename to src/language/core_lexing.cpp index 90ee5c8..44e2df7 100644 --- a/core_lexing.cpp +++ b/src/language/core_lexing.cpp @@ -1,788 +1,788 @@ -force_inline B32 token_is_assign(Token_Kind token) { return token >= TK_FirstAssign && token <= TK_LastAssign; } -force_inline B32 token_is_assign(Token *token) { return token_is_assign(token->kind); } -force_inline B32 token_is_compare(Token_Kind token) { return token >= TK_FirstCompare && token <= TK_LastCompare; } -force_inline B32 token_is_compare(Token *token) { return token_is_compare(token->kind); } - -CORE_Static U8 -lexc(Lex_Stream *s) { - return s->stream.str[s->iter]; -} - -CORE_Static U8 -lexci(Lex_Stream *s, S32 i) { - return s->stream.str[s->iter + i]; -} - -CORE_Static U8 * -lexcp(Lex_Stream *s) { - return s->stream.str + s->iter; -} - -CORE_Static B32 -lex_is_whitespace(U8 c) { - B32 result = c == ' ' || c == '\r'; - return result; -} - -CORE_Static B32 -lex_is_alphabetic(U8 c) { - B32 result = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); - return result; -} - -CORE_Static B32 -lex_is_numeric(U8 c) { - B32 result = c >= '0' && c <= '9'; - return result; -} - -CORE_Static B32 -lex_is_numeric_base16(U8 c) { - B32 result = (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || - (c >= 'a' && c <= 'f'); - return result; -} - -CORE_Static B32 -lex_is_alphanumeric(U8 c) { - B32 result = lex_is_numeric(c) || lex_is_alphabetic(c); - return result; -} - -CORE_Static void -lex_set_len(Lex_Stream *s, Token *token) { - assert(lexcp(s) >= token->str); - token->len = lexcp(s) - token->str; -} - -CORE_Static void -lex_set_keywords(Core_Ctx *lexer, Array keywords) { - Intern_String keyword = {}; - For(keywords) { - keyword = intern_string(&lexer->interns, it); - if (&it == keywords.begin()) - lexer->interns.first_keyword = keyword.str; - } - lexer->interns.last_keyword = keyword.str; -} - -CORE_Static B32 -lex_is_keyword(Intern_Table *lexer, Intern_String keyword) { - B32 result = keyword.str >= lexer->first_keyword && keyword.str <= lexer->last_keyword; - return result; -} - -CORE_Static void -token_error(Token *t, String error_val) { - t->kind = TK_Error; - t->error_val = error_val; -} - -CORE_Static void -lex_parse_u64(Core_Ctx *lexer, Token *t, S64 base) { - Scoped_Arena _scope(lexer->scratch); - Set_BigInt_Arena(lexer->scratch); - - t->kind = TK_Integer; - BigInt m = bigint_u64(1); - BigInt base_mul = bigint_u64(base); - BigInt result = bigint_u64(0); - - for (S64 i = t->len - 1; i >= 0; --i) { - U64 value = t->str[i]; - if (t->str[i] >= 'a') value = value - 'a' + 10; - else if (t->str[i] >= 'A') value = value - 'A' + 10; - else value -= '0'; - - BigInt val = bigint_u64(value); - BigInt new_val = bigint_mul(&val, &m); - result = bigint_add(&result, &new_val); - m = bigint_mul(&m, &base_mul); - } - - t->int_val = bigint_copy(lexer->perm, &result); -} - -CORE_Static void -lex_parse_f64(Token *t) { - t->kind = TK_Float; - char buffer[128]; - S64 len = clamp_top((int)t->len, 126); - memory_copy(buffer, t->str, len); - buffer[len] = 0; - t->f64_val = strtod(buffer, 0); -} - -CORE_Static void -lex_advance(Lex_Stream *s) { - if (s->iter >= s->stream.len) { - return; - } - else if (lexc(s) == '\n') { - s->iter++; - s->line++; - s->line_begin = lexcp(s); - } - else { - s->iter++; - } -} - -CORE_Static void -lex_parse_string(Lex_Stream *s, Token *t, U8 c) { - for (;;) { - if (lexc(s) == '\\') lex_advance(s); - else if (lexc(s) == c) break; - else if (lexc(s) == 0) { - token_error(t, "Unterminated string, reached end of file"_s); - break; - } - lex_advance(s); - } - if (t->kind != TK_Error) { - lex_advance(s); - lex_set_len(s, t); - } -} - -CORE_Static void -lex_parse_ident(Intern_Table *table, Lex_Stream *s, Token *t) { - while (lex_is_alphanumeric(lexc(s)) || lexc(s) == '_') - lex_advance(s); - lex_set_len(s, t); -} - -#define CASE2(op, OpName, Assign) \ - case op: \ - if (lexc(s) == '=') { \ - lex_advance(s); \ - t.kind = Assign; \ - } \ - else { \ - t.kind = OpName; \ - } \ - break -#define CASE3(op, OpName, Assign, Incr) \ - case op: \ - if (lexc(s) == '=') { \ - lex_advance(s); \ - t.kind = Assign; \ - } \ - else if (lexc(s) == op) { \ - lex_advance(s); \ - t.kind = Incr; \ - } \ - else { \ - t.kind = OpName; \ - } \ - break - -CORE_Static Token -token_make(Core_Ctx *lexer, U8 *str, Intern_String file, int line, U8 *line_begin) { - Token t = {}; - t.str = str; - t.file = file; - t.line = line; - t.line_begin = line_begin; - t.di = lexer->token_debug_ids++; - return t; -} - -CORE_Static Token -token_make(Core_Ctx *lexer) { - return token_make(lexer, lexcp(&lexer->stream), lexer->stream.file, lexer->stream.line, lexer->stream.line_begin); -} - -CORE_Static Token * -lex_last_indent_token(Lex_Stream *s) { - if (s->indent_stack.len > 0) { - return *s->indent_stack.back(); - } - return &pctx->same_scope_token; -} - -CORE_Static B32 -lex_is_scope(Token *t) { - B32 result = t->kind == OPEN_SCOPE || t->kind == CLOSE_SCOPE || t->kind == SAME_SCOPE; - return result; -} - -CORE_Static void -lex_add_token(Core_Ctx *ctx, Token *token) { - Token *top = (Token *)arena_push_size(&ctx->token_arena, sizeof(Token)); - *top = *token; - ctx->tokens.len += 1; - ctx->tokens.cap += 1; - ctx->tokens.data = (Token *)ctx->token_arena.memory.data; -} - -CORE_Static void -lex_unwind_indent_stack(Core_Ctx *ctx, Token *t, Lex_Stream *s) { - for (S64 i = s->indent_stack.len - 1; i >= 0; i -= 1) { - auto it = s->indent_stack.data[i]; - assert(lex_is_scope(it)); - if (it->indent == t->indent) { - t->kind = SAME_SCOPE; - lex_add_token(ctx, t); - break; - } - else if (it->indent < t->indent) { - token_error(t, "Bad indentation"_s); - lex_add_token(ctx, t); - break; - } - else { - s->indent_stack.pop(); - t->kind = CLOSE_SCOPE; - lex_add_token(ctx, t); - } - } -} - -CORE_Static void -lex__stream(Core_Ctx *lexer) { - Intern_Table *table = &lexer->interns; - Lex_Stream *s = &lexer->stream; - - B32 beginning = true; - for (;;) { - if (lexc(s) == 0 || s->iter >= s->stream.len) { - end_of_stream: - Token t = token_make(lexer); - lex_unwind_indent_stack(lexer, &t, s); - break; - } - - // @note: the lexer is going to be a 2 stage process - // first we tokenize the indentation and then proceed to tokenize - // the good stuff - - // for blocks of stmts we parse till we cant find another new line - // of same scope. - // parse_decl doesn't require preceding new line - // - // in that way new lines act as commas in CORE_Static params - // seeing a comma means that there is a next thing to parse - // and it's easy to parse stuff using a do while loop - - // @note: first handle indentation - // mostly we want to merge multiple new lines - // but for down scopes we want to emit 2 new lines - // that will ease out parsing, one token to break out - // from a block parsing, second to allow continuation of surrounding scope - Token t = token_make(lexer); - B32 should_emit = beginning; - for (;;) { - switch (lexc(s)) { - case 0: goto end_of_stream; break; - case '\t': - case ' ': - lex_advance(s); - t.indent++; - break; - case '\r': lex_advance(s); break; - case '/': { - if (lexci(s, 1) == '/') { - lex_advance(s); - lex_advance(s); - t.kind = TK_Comment; - for (;;) { - if (lexc(s) == '\n' || lexc(s) == 0) break; - lex_advance(s); - } - } - else if (lexci(s, 1) == '*') { - lex_advance(s); - lex_advance(s); - t.kind = TK_Comment; - for (;;) { - if (lexc(s) == '*' && lexci(s, 1) == '/') { - lex_advance(s); - lex_advance(s); - break; - } - else if (lexc(s) == 0) { - token_error(&t, "Unterminated block comment"_s); - break; - } - lex_advance(s); - } - } - else goto indent_loop_break; - } break; - - // @todo: also need some way to detect indentation so that - // first of all we can check for consistency and second of - // all because we would know by how much to indent - // @todo: after detecting indentation 2 spaces would become 1 indent value - case ';': { - Token semi = token_make(lexer); - Token *last = lex_last_indent_token(s); - semi.indent = last->indent; - lex_advance(s); - if (lexc(s) == ';') { - lex_advance(s); - semi.kind = OPEN_SCOPE; - semi.indent = last->indent + 2; // @todo: proper detection of indentation - lex_add_token(lexer, &semi); - s->indent_stack.add(lexer->tokens.back()); - } - else { - semi.kind = SAME_SCOPE; - lex_add_token(lexer, &semi); - } - } break; - - case '\n': { - lex_advance(s); - should_emit = true; - t = token_make(lexer); - } break; - - default: { - if (s->inside_brace_paren) should_emit = false; - if (should_emit) { - Token *last = lex_last_indent_token(s); - if (t.indent > last->indent) { - t.kind = OPEN_SCOPE; - lex_add_token(lexer, &t); - s->indent_stack.add(lexer->tokens.back()); - } - - else if (t.indent < last->indent) { - lex_unwind_indent_stack(lexer, &t, s); - } - else { - t.kind = SAME_SCOPE; - lex_add_token(lexer, &t); - } - } - - goto indent_loop_break; - } - } - } - indent_loop_break: - beginning = false; - - // @note: handle the indented token - t = token_make(lexer); - lex_advance(s); - switch (*t.str) { - case 0: goto end_of_stream; break; - case '@': t.kind = TK_At; break; - case '(': - s->inside_brace_paren++; - t.kind = TK_OpenParen; - break; - case ')': - s->inside_brace_paren--; - t.kind = TK_CloseParen; - break; - case '{': - s->inside_brace_paren++; - t.kind = TK_OpenBrace; - break; - case '}': - s->inside_brace_paren--; - t.kind = TK_CloseBrace; - break; - case '[': - s->inside_brace_paren++; - t.kind = TK_OpenBracket; - break; - case ']': - s->inside_brace_paren--; - t.kind = TK_CloseBracket; - break; - case ',': t.kind = TK_Comma; break; - case '~': t.kind = TK_Neg; break; - case '?': t.kind = TK_Question; break; - case '^': - t.kind = TK_BitXor; - break; - CASE2('!', TK_Not, TK_NotEquals); - CASE2('=', TK_Assign, TK_Equals); - CASE2('*', TK_Mul, TK_MulAssign); - CASE2('%', TK_Mod, TK_ModAssign); - CASE3('+', TK_Add, TK_AddAssign, TK_Increment); - CASE3('&', TK_BitAnd, TK_AndAssign, TK_And); - CASE3('|', TK_BitOr, TK_OrAssign, TK_Or); - - case '$': { - t.kind = TK_Polymorph; - lex_parse_ident(table, s, &t); - t.str += 1; - t.len -= 1; - t.intern_val = intern_string(table, t.string); - if (t.len == 0) token_error(&t, "Polymorph token without content"_s); - } break; - - case '#': { - t.kind = TK_Pound; - lex_parse_ident(table, s, &t); - t.str += 1; - t.len -= 1; - t.intern_val = intern_string(table, t.string); - if (t.len == 0) token_error(&t, "Macro token without content"_s); - } break; - - case '.': { - if (lexc(s) == '.') { - lex_advance(s); - if (lexc(s) == '.') { - lex_advance(s); - t.kind = TK_ThreeDots; - } - else t.kind = TK_TwoDots; - } - else t.kind = TK_Dot; - } break; - - case '\'': { - assert(s->stream.len >= s->iter); - UTF32_Result decode = utf8_to_utf32(lexcp(s), s->stream.len - s->iter); - if (!decode.error) { - for (S32 i = 0; i < decode.advance; i++) lex_advance(s); - t.unicode = decode.out_str; - t.kind = TK_UnicodeLit; - if (lexc(s) == '\'') { - lex_advance(s); - } - else { - token_error(&t, "Unclosed unicode literal"_s); - } - } - else { - token_error(&t, "Invalid UTF8 sequence in unicode literal"_s); - } - } break; - - case '<': { - if (lexc(s) == '<') { - lex_advance(s); - if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_LeftShiftAssign; - } - else { - t.kind = TK_LeftShift; - } - } - else if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_LesserThenOrEqual; - } - else { - t.kind = TK_LesserThen; - } - } break; - - case '>': { - if (lexc(s) == '>') { - lex_advance(s); - if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_RightShiftAssign; - } - else { - t.kind = TK_RightShift; - } - } - else if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_GreaterThenOrEqual; - } - else { - t.kind = TK_GreaterThen; - } - } break; - - case ':': { - if (lexc(s) == ':') { - lex_advance(s); - t.kind = TK_DoubleColon; - } - else if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_ColonAssign; - } - else { - t.kind = TK_Colon; - } - } break; - - case '-': { - if (lexc(s) == '=') { - lex_advance(s); - t.kind = TK_SubAssign; - } - else if (lexc(s) == '-') { - lex_advance(s); - t.kind = TK_Decrement; - } - else if (lexc(s) == '>') { - lex_advance(s); - t.kind = TK_Arrow; - } - else { - t.kind = TK_Sub; - } - } break; - - case '"': { - t.kind = TK_StringLit; - lex_parse_string(s, &t, '"'); - if (t.kind != TK_Error) { - t.str += 1; - t.len -= 2; - } - t.intern_val = intern_string(table, t.string); - } break; - - case '/': { - if (lexc(s) == '=') { - t.kind = TK_DivAssign; - lex_advance(s); - } - else { - t.kind = TK_Div; - } - } break; - - case '0': { - if (lexc(s) == 'x') { - lex_advance(s); - while (lex_is_numeric_base16(lexc(s))) - lex_advance(s); - lex_set_len(s, &t); - t.str += 2; - t.len -= 2; - if (t.len == 0) - token_error(&t, "Hex constant doesn't have value"_s); - else - lex_parse_u64(lexer, &t, 16); - break; - } - else if (lexc(s) == 'b') { - lex_advance(s); - while (lexc(s) == '0' || lexc(s) == '1') - lex_advance(s); - lex_set_len(s, &t); - t.str += 2; - t.len -= 2; - if (t.len == 0) - token_error(&t, "Hex constant doesn't have value"_s); - else - lex_parse_u64(lexer, &t, 2); - break; - } - } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - B32 found_dot = false; - for (;;) { - if (lex_is_numeric(lexc(s))) - ; - else if (lexc(s) == '.') { - if (found_dot) { - token_error(&t, "Multiple '.' in float literal"_s); - goto end_of_switch; - } - found_dot = true; - } - else break; - - lex_advance(s); - } - lex_set_len(s, &t); - if (found_dot) lex_parse_f64(&t); - else lex_parse_u64(lexer, &t, 10); - - } break; - - case 'A': - case 'a': - case 'M': - case 'm': - case 'B': - case 'b': - case 'N': - case 'n': - case 'C': - case 'c': - case 'O': - case 'o': - case 'D': - case 'd': - case 'P': - case 'p': - case 'E': - case 'e': - case 'Q': - case 'q': - case 'F': - case 'f': - case 'R': - case 'r': - case 'G': - case 'g': - case 'S': - case 's': - case 'H': - case 'h': - case 'T': - case 't': - case 'I': - case 'i': - case 'U': - case 'u': - case 'J': - case 'j': - case 'V': - case 'v': - case 'K': - case 'k': - case 'W': - case 'w': - case 'L': - case 'X': - case 'l': - case 'x': - case 'Z': - case 'z': - case 'Y': - case 'y': - case '_': { - t.kind = TK_Identifier; - lex_parse_ident(table, s, &t); - t.intern_val = intern_string(table, t.string); - if (lex_is_keyword(table, t.intern_val)) { - t.kind = TK_Keyword; - } - } break; - - default: { - token_error(&t, "Unknown token"_s); - } - } - end_of_switch: - - if (t.len == 0) - lex_set_len(s, &t); - - lex_add_token(lexer, &t); - } -#undef CASE2 -#undef CASE3 -} - -CORE_Static void -lex_restream(Core_Ctx *lexer, String istream, String file) { - lexer->stream = {}; - lexer->stream.stream = istream; - lexer->stream.line_begin = istream.str; - lexer->stream.file = lexer->intern(file); - - Scoped_Arena _scope(lexer->scratch); - lexer->stream.indent_stack.allocator = lexer->scratch; - lexer->stream.indent_stack.add(&lexer->same_scope_token); - lex__stream(lexer); - lexer->lines_lexed += lexer->stream.line; -} - -//----------------------------------------------------------------------------- -// Token metadata -//----------------------------------------------------------------------------- - -CORE_Static const char * -name(Token_Kind kind) { - switch (kind) { - case TK_End: - return "End of stream"; - /*# -import meta -for i in meta.token_kinds: - if i[1] != "SPECIAL": - print("case TK_" + i[0] + f": return \"{i[1]}\";") - */ - case TK_Mul: return "*"; - case TK_Div: return "/"; - case TK_Mod: return "%"; - case TK_LeftShift: return "<<"; - case TK_RightShift: return ">>"; - case TK_Add: return "+"; - case TK_Sub: return "-"; - case TK_Equals: return "=="; - case TK_LesserThenOrEqual: return "<="; - case TK_GreaterThenOrEqual: return ">="; - case TK_LesserThen: return "<"; - case TK_GreaterThen: return ">"; - case TK_NotEquals: return "!="; - case TK_BitAnd: return "&"; - case TK_BitOr: return "|"; - case TK_BitXor: return "^"; - case TK_And: return "&&"; - case TK_Or: return "||"; - case TK_Neg: return "~"; - case TK_Not: return "!"; - case TK_Decrement: return "--"; - case TK_Increment: return "++"; - case TK_PostDecrement: return "--"; - case TK_PostIncrement: return "++"; - case TK_Assign: return "="; - case TK_ColonAssign: return ":="; - case TK_DivAssign: return "/="; - case TK_MulAssign: return "*="; - case TK_ModAssign: return "%="; - case TK_SubAssign: return "-="; - case TK_AddAssign: return "+="; - case TK_AndAssign: return "&="; - case TK_OrAssign: return "|="; - case TK_XorAssign: return "^="; - case TK_LeftShiftAssign: return "<<="; - case TK_RightShiftAssign: return ">>="; - case TK_OpenParen: return "("; - case TK_CloseParen: return ")"; - case TK_OpenBrace: return "{"; - case TK_CloseBrace: return "}"; - case TK_OpenBracket: return "["; - case TK_CloseBracket: return "]"; - case TK_Comma: return ","; - case TK_Pound: return "#"; - case TK_Question: return "?"; - case TK_ThreeDots: return "..."; - case TK_Semicolon: return ";"; - case TK_Dot: return "."; - case TK_TwoDots: return ".."; - case TK_NewLine: return "[NewLine]"; - case TK_Colon: return ":"; - case TK_DoubleColon: return "::"; - case TK_At: return "@"; - case TK_Arrow: return "->"; - case TK_Polymorph: return "$"; - case TK_ExprSizeof: return "[sizeof]"; - case TK_DocComment: return "[///]"; - case TK_Comment: return "//"; - case TK_Identifier: return "[Ident]"; - case TK_UnicodeLit: return "[Unicode]"; - case TK_StringLit: return "[String]"; - case TK_Error: return "[Error]"; - case TK_Float: return "[Float]"; - case TK_Integer: return "[Int]"; - case TK_Keyword: - return "[Keyword]"; - /*END*/ - case CLOSE_SCOPE: return "Close_Scope"; - case OPEN_SCOPE: return "Open_Scope"; - case SAME_SCOPE: return "Same_Scope"; - default: invalid_codepath; return ""; - } -} +force_inline B32 token_is_assign(Token_Kind token) { return token >= TK_FirstAssign && token <= TK_LastAssign; } +force_inline B32 token_is_assign(Token *token) { return token_is_assign(token->kind); } +force_inline B32 token_is_compare(Token_Kind token) { return token >= TK_FirstCompare && token <= TK_LastCompare; } +force_inline B32 token_is_compare(Token *token) { return token_is_compare(token->kind); } + +CORE_Static U8 +lexc(Lex_Stream *s) { + return s->stream.str[s->iter]; +} + +CORE_Static U8 +lexci(Lex_Stream *s, S32 i) { + return s->stream.str[s->iter + i]; +} + +CORE_Static U8 * +lexcp(Lex_Stream *s) { + return s->stream.str + s->iter; +} + +CORE_Static B32 +lex_is_whitespace(U8 c) { + B32 result = c == ' ' || c == '\r'; + return result; +} + +CORE_Static B32 +lex_is_alphabetic(U8 c) { + B32 result = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + return result; +} + +CORE_Static B32 +lex_is_numeric(U8 c) { + B32 result = c >= '0' && c <= '9'; + return result; +} + +CORE_Static B32 +lex_is_numeric_base16(U8 c) { + B32 result = (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || + (c >= 'a' && c <= 'f'); + return result; +} + +CORE_Static B32 +lex_is_alphanumeric(U8 c) { + B32 result = lex_is_numeric(c) || lex_is_alphabetic(c); + return result; +} + +CORE_Static void +lex_set_len(Lex_Stream *s, Token *token) { + assert(lexcp(s) >= token->str); + token->len = lexcp(s) - token->str; +} + +CORE_Static void +lex_set_keywords(Core_Ctx *lexer, Array keywords) { + Intern_String keyword = {}; + For(keywords) { + keyword = intern_string(&lexer->interns, it); + if (&it == keywords.begin()) + lexer->interns.first_keyword = keyword.str; + } + lexer->interns.last_keyword = keyword.str; +} + +CORE_Static B32 +lex_is_keyword(Intern_Table *lexer, Intern_String keyword) { + B32 result = keyword.str >= lexer->first_keyword && keyword.str <= lexer->last_keyword; + return result; +} + +CORE_Static void +token_error(Token *t, String error_val) { + t->kind = TK_Error; + t->error_val = error_val; +} + +CORE_Static void +lex_parse_u64(Core_Ctx *lexer, Token *t, S64 base) { + Scoped_Arena _scope(lexer->scratch); + Set_BigInt_Arena(lexer->scratch); + + t->kind = TK_Integer; + BigInt m = bigint_u64(1); + BigInt base_mul = bigint_u64(base); + BigInt result = bigint_u64(0); + + for (S64 i = t->len - 1; i >= 0; --i) { + U64 value = t->str[i]; + if (t->str[i] >= 'a') value = value - 'a' + 10; + else if (t->str[i] >= 'A') value = value - 'A' + 10; + else value -= '0'; + + BigInt val = bigint_u64(value); + BigInt new_val = bigint_mul(&val, &m); + result = bigint_add(&result, &new_val); + m = bigint_mul(&m, &base_mul); + } + + t->int_val = bigint_copy(lexer->perm, &result); +} + +CORE_Static void +lex_parse_f64(Token *t) { + t->kind = TK_Float; + char buffer[128]; + S64 len = clamp_top((int)t->len, 126); + memory_copy(buffer, t->str, len); + buffer[len] = 0; + t->f64_val = strtod(buffer, 0); +} + +CORE_Static void +lex_advance(Lex_Stream *s) { + if (s->iter >= s->stream.len) { + return; + } + else if (lexc(s) == '\n') { + s->iter++; + s->line++; + s->line_begin = lexcp(s); + } + else { + s->iter++; + } +} + +CORE_Static void +lex_parse_string(Lex_Stream *s, Token *t, U8 c) { + for (;;) { + if (lexc(s) == '\\') lex_advance(s); + else if (lexc(s) == c) break; + else if (lexc(s) == 0) { + token_error(t, "Unterminated string, reached end of file"_s); + break; + } + lex_advance(s); + } + if (t->kind != TK_Error) { + lex_advance(s); + lex_set_len(s, t); + } +} + +CORE_Static void +lex_parse_ident(Intern_Table *table, Lex_Stream *s, Token *t) { + while (lex_is_alphanumeric(lexc(s)) || lexc(s) == '_') + lex_advance(s); + lex_set_len(s, t); +} + +#define CASE2(op, OpName, Assign) \ + case op: \ + if (lexc(s) == '=') { \ + lex_advance(s); \ + t.kind = Assign; \ + } \ + else { \ + t.kind = OpName; \ + } \ + break +#define CASE3(op, OpName, Assign, Incr) \ + case op: \ + if (lexc(s) == '=') { \ + lex_advance(s); \ + t.kind = Assign; \ + } \ + else if (lexc(s) == op) { \ + lex_advance(s); \ + t.kind = Incr; \ + } \ + else { \ + t.kind = OpName; \ + } \ + break + +CORE_Static Token +token_make(Core_Ctx *lexer, U8 *str, Intern_String file, int line, U8 *line_begin) { + Token t = {}; + t.str = str; + t.file = file; + t.line = line; + t.line_begin = line_begin; + t.di = lexer->token_debug_ids++; + return t; +} + +CORE_Static Token +token_make(Core_Ctx *lexer) { + return token_make(lexer, lexcp(&lexer->stream), lexer->stream.file, lexer->stream.line, lexer->stream.line_begin); +} + +CORE_Static Token * +lex_last_indent_token(Lex_Stream *s) { + if (s->indent_stack.len > 0) { + return *s->indent_stack.back(); + } + return &pctx->same_scope_token; +} + +CORE_Static B32 +lex_is_scope(Token *t) { + B32 result = t->kind == OPEN_SCOPE || t->kind == CLOSE_SCOPE || t->kind == SAME_SCOPE; + return result; +} + +CORE_Static void +lex_add_token(Core_Ctx *ctx, Token *token) { + Token *top = (Token *)arena_push_size(&ctx->token_arena, sizeof(Token)); + *top = *token; + ctx->tokens.len += 1; + ctx->tokens.cap += 1; + ctx->tokens.data = (Token *)ctx->token_arena.memory.data; +} + +CORE_Static void +lex_unwind_indent_stack(Core_Ctx *ctx, Token *t, Lex_Stream *s) { + for (S64 i = s->indent_stack.len - 1; i >= 0; i -= 1) { + auto it = s->indent_stack.data[i]; + assert(lex_is_scope(it)); + if (it->indent == t->indent) { + t->kind = SAME_SCOPE; + lex_add_token(ctx, t); + break; + } + else if (it->indent < t->indent) { + token_error(t, "Bad indentation"_s); + lex_add_token(ctx, t); + break; + } + else { + s->indent_stack.pop(); + t->kind = CLOSE_SCOPE; + lex_add_token(ctx, t); + } + } +} + +CORE_Static void +lex__stream(Core_Ctx *lexer) { + Intern_Table *table = &lexer->interns; + Lex_Stream *s = &lexer->stream; + + B32 beginning = true; + for (;;) { + if (lexc(s) == 0 || s->iter >= s->stream.len) { + end_of_stream: + Token t = token_make(lexer); + lex_unwind_indent_stack(lexer, &t, s); + break; + } + + // @note: the lexer is going to be a 2 stage process + // first we tokenize the indentation and then proceed to tokenize + // the good stuff + + // for blocks of stmts we parse till we cant find another new line + // of same scope. + // parse_decl doesn't require preceding new line + // + // in that way new lines act as commas in CORE_Static params + // seeing a comma means that there is a next thing to parse + // and it's easy to parse stuff using a do while loop + + // @note: first handle indentation + // mostly we want to merge multiple new lines + // but for down scopes we want to emit 2 new lines + // that will ease out parsing, one token to break out + // from a block parsing, second to allow continuation of surrounding scope + Token t = token_make(lexer); + B32 should_emit = beginning; + for (;;) { + switch (lexc(s)) { + case 0: goto end_of_stream; break; + case '\t': + case ' ': + lex_advance(s); + t.indent++; + break; + case '\r': lex_advance(s); break; + case '/': { + if (lexci(s, 1) == '/') { + lex_advance(s); + lex_advance(s); + t.kind = TK_Comment; + for (;;) { + if (lexc(s) == '\n' || lexc(s) == 0) break; + lex_advance(s); + } + } + else if (lexci(s, 1) == '*') { + lex_advance(s); + lex_advance(s); + t.kind = TK_Comment; + for (;;) { + if (lexc(s) == '*' && lexci(s, 1) == '/') { + lex_advance(s); + lex_advance(s); + break; + } + else if (lexc(s) == 0) { + token_error(&t, "Unterminated block comment"_s); + break; + } + lex_advance(s); + } + } + else goto indent_loop_break; + } break; + + // @todo: also need some way to detect indentation so that + // first of all we can check for consistency and second of + // all because we would know by how much to indent + // @todo: after detecting indentation 2 spaces would become 1 indent value + case ';': { + Token semi = token_make(lexer); + Token *last = lex_last_indent_token(s); + semi.indent = last->indent; + lex_advance(s); + if (lexc(s) == ';') { + lex_advance(s); + semi.kind = OPEN_SCOPE; + semi.indent = last->indent + 2; // @todo: proper detection of indentation + lex_add_token(lexer, &semi); + s->indent_stack.add(lexer->tokens.back()); + } + else { + semi.kind = SAME_SCOPE; + lex_add_token(lexer, &semi); + } + } break; + + case '\n': { + lex_advance(s); + should_emit = true; + t = token_make(lexer); + } break; + + default: { + if (s->inside_brace_paren) should_emit = false; + if (should_emit) { + Token *last = lex_last_indent_token(s); + if (t.indent > last->indent) { + t.kind = OPEN_SCOPE; + lex_add_token(lexer, &t); + s->indent_stack.add(lexer->tokens.back()); + } + + else if (t.indent < last->indent) { + lex_unwind_indent_stack(lexer, &t, s); + } + else { + t.kind = SAME_SCOPE; + lex_add_token(lexer, &t); + } + } + + goto indent_loop_break; + } + } + } + indent_loop_break: + beginning = false; + + // @note: handle the indented token + t = token_make(lexer); + lex_advance(s); + switch (*t.str) { + case 0: goto end_of_stream; break; + case '@': t.kind = TK_At; break; + case '(': + s->inside_brace_paren++; + t.kind = TK_OpenParen; + break; + case ')': + s->inside_brace_paren--; + t.kind = TK_CloseParen; + break; + case '{': + s->inside_brace_paren++; + t.kind = TK_OpenBrace; + break; + case '}': + s->inside_brace_paren--; + t.kind = TK_CloseBrace; + break; + case '[': + s->inside_brace_paren++; + t.kind = TK_OpenBracket; + break; + case ']': + s->inside_brace_paren--; + t.kind = TK_CloseBracket; + break; + case ',': t.kind = TK_Comma; break; + case '~': t.kind = TK_Neg; break; + case '?': t.kind = TK_Question; break; + case '^': + t.kind = TK_BitXor; + break; + CASE2('!', TK_Not, TK_NotEquals); + CASE2('=', TK_Assign, TK_Equals); + CASE2('*', TK_Mul, TK_MulAssign); + CASE2('%', TK_Mod, TK_ModAssign); + CASE3('+', TK_Add, TK_AddAssign, TK_Increment); + CASE3('&', TK_BitAnd, TK_AndAssign, TK_And); + CASE3('|', TK_BitOr, TK_OrAssign, TK_Or); + + case '$': { + t.kind = TK_Polymorph; + lex_parse_ident(table, s, &t); + t.str += 1; + t.len -= 1; + t.intern_val = intern_string(table, t.string); + if (t.len == 0) token_error(&t, "Polymorph token without content"_s); + } break; + + case '#': { + t.kind = TK_Pound; + lex_parse_ident(table, s, &t); + t.str += 1; + t.len -= 1; + t.intern_val = intern_string(table, t.string); + if (t.len == 0) token_error(&t, "Macro token without content"_s); + } break; + + case '.': { + if (lexc(s) == '.') { + lex_advance(s); + if (lexc(s) == '.') { + lex_advance(s); + t.kind = TK_ThreeDots; + } + else t.kind = TK_TwoDots; + } + else t.kind = TK_Dot; + } break; + + case '\'': { + assert(s->stream.len >= s->iter); + UTF32_Result decode = utf8_to_utf32(lexcp(s), s->stream.len - s->iter); + if (!decode.error) { + for (S32 i = 0; i < decode.advance; i++) lex_advance(s); + t.unicode = decode.out_str; + t.kind = TK_UnicodeLit; + if (lexc(s) == '\'') { + lex_advance(s); + } + else { + token_error(&t, "Unclosed unicode literal"_s); + } + } + else { + token_error(&t, "Invalid UTF8 sequence in unicode literal"_s); + } + } break; + + case '<': { + if (lexc(s) == '<') { + lex_advance(s); + if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_LeftShiftAssign; + } + else { + t.kind = TK_LeftShift; + } + } + else if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_LesserThenOrEqual; + } + else { + t.kind = TK_LesserThen; + } + } break; + + case '>': { + if (lexc(s) == '>') { + lex_advance(s); + if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_RightShiftAssign; + } + else { + t.kind = TK_RightShift; + } + } + else if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_GreaterThenOrEqual; + } + else { + t.kind = TK_GreaterThen; + } + } break; + + case ':': { + if (lexc(s) == ':') { + lex_advance(s); + t.kind = TK_DoubleColon; + } + else if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_ColonAssign; + } + else { + t.kind = TK_Colon; + } + } break; + + case '-': { + if (lexc(s) == '=') { + lex_advance(s); + t.kind = TK_SubAssign; + } + else if (lexc(s) == '-') { + lex_advance(s); + t.kind = TK_Decrement; + } + else if (lexc(s) == '>') { + lex_advance(s); + t.kind = TK_Arrow; + } + else { + t.kind = TK_Sub; + } + } break; + + case '"': { + t.kind = TK_StringLit; + lex_parse_string(s, &t, '"'); + if (t.kind != TK_Error) { + t.str += 1; + t.len -= 2; + } + t.intern_val = intern_string(table, t.string); + } break; + + case '/': { + if (lexc(s) == '=') { + t.kind = TK_DivAssign; + lex_advance(s); + } + else { + t.kind = TK_Div; + } + } break; + + case '0': { + if (lexc(s) == 'x') { + lex_advance(s); + while (lex_is_numeric_base16(lexc(s))) + lex_advance(s); + lex_set_len(s, &t); + t.str += 2; + t.len -= 2; + if (t.len == 0) + token_error(&t, "Hex constant doesn't have value"_s); + else + lex_parse_u64(lexer, &t, 16); + break; + } + else if (lexc(s) == 'b') { + lex_advance(s); + while (lexc(s) == '0' || lexc(s) == '1') + lex_advance(s); + lex_set_len(s, &t); + t.str += 2; + t.len -= 2; + if (t.len == 0) + token_error(&t, "Hex constant doesn't have value"_s); + else + lex_parse_u64(lexer, &t, 2); + break; + } + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + B32 found_dot = false; + for (;;) { + if (lex_is_numeric(lexc(s))) + ; + else if (lexc(s) == '.') { + if (found_dot) { + token_error(&t, "Multiple '.' in float literal"_s); + goto end_of_switch; + } + found_dot = true; + } + else break; + + lex_advance(s); + } + lex_set_len(s, &t); + if (found_dot) lex_parse_f64(&t); + else lex_parse_u64(lexer, &t, 10); + + } break; + + case 'A': + case 'a': + case 'M': + case 'm': + case 'B': + case 'b': + case 'N': + case 'n': + case 'C': + case 'c': + case 'O': + case 'o': + case 'D': + case 'd': + case 'P': + case 'p': + case 'E': + case 'e': + case 'Q': + case 'q': + case 'F': + case 'f': + case 'R': + case 'r': + case 'G': + case 'g': + case 'S': + case 's': + case 'H': + case 'h': + case 'T': + case 't': + case 'I': + case 'i': + case 'U': + case 'u': + case 'J': + case 'j': + case 'V': + case 'v': + case 'K': + case 'k': + case 'W': + case 'w': + case 'L': + case 'X': + case 'l': + case 'x': + case 'Z': + case 'z': + case 'Y': + case 'y': + case '_': { + t.kind = TK_Identifier; + lex_parse_ident(table, s, &t); + t.intern_val = intern_string(table, t.string); + if (lex_is_keyword(table, t.intern_val)) { + t.kind = TK_Keyword; + } + } break; + + default: { + token_error(&t, "Unknown token"_s); + } + } + end_of_switch: + + if (t.len == 0) + lex_set_len(s, &t); + + lex_add_token(lexer, &t); + } +#undef CASE2 +#undef CASE3 +} + +CORE_Static void +lex_restream(Core_Ctx *lexer, String istream, String file) { + lexer->stream = {}; + lexer->stream.stream = istream; + lexer->stream.line_begin = istream.str; + lexer->stream.file = lexer->intern(file); + + Scoped_Arena _scope(lexer->scratch); + lexer->stream.indent_stack.allocator = lexer->scratch; + lexer->stream.indent_stack.add(&lexer->same_scope_token); + lex__stream(lexer); + lexer->lines_lexed += lexer->stream.line; +} + +//----------------------------------------------------------------------------- +// Token metadata +//----------------------------------------------------------------------------- + +CORE_Static const char * +name(Token_Kind kind) { + switch (kind) { + case TK_End: + return "End of stream"; + /*# +import meta +for i in meta.token_kinds: + if i[1] != "SPECIAL": + print("case TK_" + i[0] + f": return \"{i[1]}\";") + */ + case TK_Mul: return "*"; + case TK_Div: return "/"; + case TK_Mod: return "%"; + case TK_LeftShift: return "<<"; + case TK_RightShift: return ">>"; + case TK_Add: return "+"; + case TK_Sub: return "-"; + case TK_Equals: return "=="; + case TK_LesserThenOrEqual: return "<="; + case TK_GreaterThenOrEqual: return ">="; + case TK_LesserThen: return "<"; + case TK_GreaterThen: return ">"; + case TK_NotEquals: return "!="; + case TK_BitAnd: return "&"; + case TK_BitOr: return "|"; + case TK_BitXor: return "^"; + case TK_And: return "&&"; + case TK_Or: return "||"; + case TK_Neg: return "~"; + case TK_Not: return "!"; + case TK_Decrement: return "--"; + case TK_Increment: return "++"; + case TK_PostDecrement: return "--"; + case TK_PostIncrement: return "++"; + case TK_Assign: return "="; + case TK_ColonAssign: return ":="; + case TK_DivAssign: return "/="; + case TK_MulAssign: return "*="; + case TK_ModAssign: return "%="; + case TK_SubAssign: return "-="; + case TK_AddAssign: return "+="; + case TK_AndAssign: return "&="; + case TK_OrAssign: return "|="; + case TK_XorAssign: return "^="; + case TK_LeftShiftAssign: return "<<="; + case TK_RightShiftAssign: return ">>="; + case TK_OpenParen: return "("; + case TK_CloseParen: return ")"; + case TK_OpenBrace: return "{"; + case TK_CloseBrace: return "}"; + case TK_OpenBracket: return "["; + case TK_CloseBracket: return "]"; + case TK_Comma: return ","; + case TK_Pound: return "#"; + case TK_Question: return "?"; + case TK_ThreeDots: return "..."; + case TK_Semicolon: return ";"; + case TK_Dot: return "."; + case TK_TwoDots: return ".."; + case TK_NewLine: return "[NewLine]"; + case TK_Colon: return ":"; + case TK_DoubleColon: return "::"; + case TK_At: return "@"; + case TK_Arrow: return "->"; + case TK_Polymorph: return "$"; + case TK_ExprSizeof: return "[sizeof]"; + case TK_DocComment: return "[///]"; + case TK_Comment: return "//"; + case TK_Identifier: return "[Ident]"; + case TK_UnicodeLit: return "[Unicode]"; + case TK_StringLit: return "[String]"; + case TK_Error: return "[Error]"; + case TK_Float: return "[Float]"; + case TK_Integer: return "[Int]"; + case TK_Keyword: + return "[Keyword]"; + /*END*/ + case CLOSE_SCOPE: return "Close_Scope"; + case OPEN_SCOPE: return "Open_Scope"; + case SAME_SCOPE: return "Same_Scope"; + default: invalid_codepath; return ""; + } +} diff --git a/core_main.cpp b/src/language/core_main.cpp similarity index 88% rename from core_main.cpp rename to src/language/core_main.cpp index 7814846..b0c8d3f 100644 --- a/core_main.cpp +++ b/src/language/core_main.cpp @@ -1,175 +1,176 @@ -/* - -Features -- Add := for default arguments in lambda. (a := false, b := false) -- Add struct default values. -- Add for loop: - - for i: int = 0, i < 10, i+=1 - - for diff: array - - iterator how to do this without function poly ? for it := iter_begin(), iter_valid(), iter_advance() -- Expand macros - -- Using directive to bring symbols into local scope -- Other kinds of casts, a cast from structs of same layout, a cast without conversion -- Inject symbols into a declaration / namespace ?? -- Optionally pass size and alignment calculations to C ? - -Cleanup -- Look into unifying lambda expressions and lambda declarations -- Ast_Module shouldn't require Ast_File to hold declarations etc. I think - -- Look into a different way of passing arguments in typechecking -- Separate out the codegen stage cause that can change -- Robust c declaration generation -- Nicer / more readable expression generation -- Detecting if return was called - -- Look into String_Builders in Core_Ctx -- Look into stage allocator and perhaps -use it more often to reduce scenarios -with 2 allocators, and simplify stuff -- Clean way to free all memory and reset the compiler -- Bring the table? - -- Nice way of creating valid declarations programatically -core_create_namespace("memes", { - core_basic_type( -}, 4); - -Robustness -- Test suite that expects test to error out -- Fix and decide what to do when initializing global variable using not constants C:/AProgramming/cparse/compiler/modules/Language.core:180:28: error: initializer element is not a compile-time constant -- Test and bulletproof any, slices -- This error is valid error case when someone creates a compound out of basic type C:/AProgramming/cparse/compiler/examples/arms_race/arms_race.core:137 Internal compiler error: Invalid type was passed to the compound expression, should have been an array, struct or slice - result := String{data, filesize} - -Bytecode interpreter -- Ir -- Interpreter -- Code generation - -Fun -- Inject stack traces into the program -- Inject instrumentation into the program - -Ideas -- Constant arrays that evaluate fully at compile time -- Rust like enum where you associate values(other structs) with key - -*/ - -#include "core_compiler_includes.cpp" - -const U32 COMPILE_NULL = 0x0; -const U32 COMPILE_PRINT_STATS = 0x1; -const U32 COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY = 0x2; -const U32 COMPILE_AND_RUN = 0x4; -const U32 COMPILE_TESTING = 0x8; -const U32 DONT_USE_C_COMPILER = 0x10; - -static void compile_file(Allocator *allocator, String filename, U32 compile_flags = COMPILE_NULL) { - if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { - printf("%s - ", filename.str); - } - String result = compile_file_to_string(allocator, filename); - - B32 r = os_write_file("generated_main.c"_s, result); - assert(r); - F64 total_compiler_time = os_time() - pctx->time.start; - printf("%f - ", total_compiler_time); - - Scoped_Arena scratch(pctx->scratch); - - F64 begin = os_time(); - if (!is_flag_set(compile_flags, DONT_USE_C_COMPILER)) { - String_Builder builder = {scratch.arena}; - builder.addf("clang generated_main.c -Wall -Wno-unused-function -Wno-parentheses-equality -g -o a" OS_EXE " "); - For(pctx->files_to_link) { - builder.addf("-l%Q ", it->intern_val); - } - String compiler_call = string_flatten(scratch.arena, &builder); - system((const char *)compiler_call.str); - printf("%s\n", compiler_call.str); - } - F64 end = os_time(); - - if (is_flag_set(compile_flags, COMPILE_PRINT_STATS)) { - printf("total = %f\n", os_time() - pctx->time.start); - printf("core_total = %f\n", pctx->time.total); - if (!is_flag_set(compile_flags, DONT_USE_C_COMPILER)) printf("clang = %f\n", end - begin); - printf("parsing = %f\n", pctx->time.parsing); - printf("typecheck = %f\n", pctx->time.typechecking); - printf("generatin = %f\n", pctx->time.code_generation); - printf("lines lexed= %d\n", pctx->lines_lexed); - } - - if (is_flag_set(compile_flags, COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY)) { - // @! Allocator stats - } - - if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { - String testing = compile_flags & COMPILE_TESTING ? "testing"_s : ""_s; -#if OS_WINDOWS - String sys = string_fmt(scratch.arena, "a.exe %Q", testing); -#else - String sys = string_fmt(scratch.arena, "./a.out %Q", testing); -#endif - int result = system((char *)sys.str); - assert(result != -1); - if (result == 0) { - printf(PRINTF_GREEN "OK!" PRINTF_RESET); - } - if (result != 0) { - printf(PRINTF_RED "ERROR!" PRINTF_RESET); - } - printf("\n"); - } -} - -int main(int argument_count, char **arguments) { - os_enable_console_colors(); - Arena arena = {}; - Arena scratch = {}; - arena_init(&arena, "Pernament arena"_s); - arena_init(&scratch, "Pernament scratch arena"_s); - - Array args = {&scratch}; - for (int i = 1; i < argument_count; i += 1) { - String arg = string_from_cstring(arguments[i]); - args.add(arg); - } - - if (!args.len) { - printf("Please specify a file to compile!"); - return 0; - } - - For(args) { - if (it == "-testing"_s) { - Scoped_Arena _scope(&scratch); - Array examples = os_list_dir(&scratch, &scratch, "examples"_s); - Array tests = os_list_dir(&scratch, &scratch, "tests"_s); - For(examples) { - if (it.is_directory) continue; - String filename = string_skip_to_last_slash(it.relative_path); - if (filename.len && filename.str[0] == '_') continue; - compile_file(&arena, it.absolute_path, COMPILE_AND_RUN | COMPILE_TESTING); - } - For(tests) { - if (it.is_directory) continue; - compile_file(&arena, it.absolute_path, COMPILE_AND_RUN | COMPILE_TESTING); - } - } - - else { - String program_name = string_from_cstring(arguments[1]); - compile_file(&arena, program_name, COMPILE_PRINT_STATS | DONT_USE_C_COMPILER); - } - } - printf("End of program\n"); - if (IsDebuggerPresent()) { - Breakpoint; - } - - return 0; -} +/* + +Features +- Add := for default arguments in lambda. (a := false, b := false) +- Add struct default values. +- Add for loop: + - for i: int = 0, i < 10, i+=1 + - for diff: array + - iterator how to do this without function poly ? for it := iter_begin(), iter_valid(), iter_advance() +- Expand macros + +- Using directive to bring symbols into local scope +- Other kinds of casts, a cast from structs of same layout, a cast without conversion +- Inject symbols into a declaration / namespace ?? +- Optionally pass size and alignment calculations to C ? + +Cleanup +- Look into unifying lambda expressions and lambda declarations +- Ast_Module shouldn't require Ast_File to hold declarations etc. I think + +- Look into a different way of passing arguments in typechecking +- Separate out the codegen stage cause that can change +- Robust c declaration generation +- Nicer / more readable expression generation +- Detecting if return was called + +- Look into String_Builders in Core_Ctx +- Look into stage allocator and perhaps +use it more often to reduce scenarios +with 2 allocators, and simplify stuff +- Clean way to free all memory and reset the compiler +- Bring the table? + +- Nice way of creating valid declarations programatically +core_create_namespace("memes", { + core_basic_type( +}, 4); + +Robustness +- Test suite that expects test to error out +- Fix and decide what to do when initializing global variable using not constants C:/AProgramming/cparse/compiler/modules/Language.core:180:28: error: initializer element is not a compile-time constant +- Test and bulletproof any, slices +- This error is valid error case when someone creates a compound out of basic type C:/AProgramming/cparse/compiler/examples/arms_race/arms_race.core:137 Internal compiler error: Invalid type was passed to the compound expression, should have been an array, struct or slice + result := String{data, filesize} + +Bytecode interpreter +- Ir +- Interpreter +- Code generation + +Fun +- Inject stack traces into the program +- Inject instrumentation into the program + +Ideas +- Constant arrays that evaluate fully at compile time +- Rust like enum where you associate values(other structs) with key + +*/ + +#include "core_compiler_includes.cpp" + +const U32 COMPILE_NULL = 0x0; +const U32 COMPILE_PRINT_STATS = 0x1; +const U32 COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY = 0x2; +const U32 COMPILE_AND_RUN = 0x4; +const U32 COMPILE_TESTING = 0x8; +const U32 DONT_USE_C_COMPILER = 0x10; + +static void compile_file(Allocator *allocator, String filename, U32 compile_flags = COMPILE_NULL) { + if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { + printf("%s - ", filename.str); + } + String result = compile_file_to_string(allocator, filename); + + B32 r = os_write_file("generated_main.c"_s, result); + assert(r); + F64 total_compiler_time = os_time() - pctx->time.start; + printf("%f - ", total_compiler_time); + + Scoped_Arena scratch(pctx->scratch); + + F64 begin = os_time(); + if (!is_flag_set(compile_flags, DONT_USE_C_COMPILER)) { + String_Builder builder = {scratch.arena}; + builder.addf("clang generated_main.c -Wall -Wno-unused-function -Wno-parentheses-equality -g -o a" OS_EXE " "); + For(pctx->files_to_link) { + builder.addf("-l%Q ", it->intern_val); + } + String compiler_call = string_flatten(scratch.arena, &builder); + system((const char *)compiler_call.str); + printf("%s\n", compiler_call.str); + } + F64 end = os_time(); + + if (is_flag_set(compile_flags, COMPILE_PRINT_STATS)) { + F64 total = os_time() - pctx->time.start; + printf("total = %f\n", total); + printf("core_total = %f\n", pctx->time.total); + if (!is_flag_set(compile_flags, DONT_USE_C_COMPILER)) printf("clang = %f\n", end - begin); + printf("parsing = %f\n", pctx->time.parsing); + printf("typecheck = %f\n", pctx->time.typechecking); + printf("generatin = %f\n", pctx->time.code_generation); + printf("lines lexed= %d\n", pctx->lines_lexed); + } + + if (is_flag_set(compile_flags, COMPILE_PRINT_ALLOCATOR_STATS_BEFORE_DESTROY)) { + // @! Allocator stats + } + + if (is_flag_set(compile_flags, COMPILE_AND_RUN)) { + String testing = compile_flags & COMPILE_TESTING ? "testing"_s : ""_s; +#if OS_WINDOWS + String sys = string_fmt(scratch.arena, "a.exe %Q", testing); +#else + String sys = string_fmt(scratch.arena, "./a.out %Q", testing); +#endif + int result = system((char *)sys.str); + assert(result != -1); + if (result == 0) { + printf(PRINTF_GREEN "OK!" PRINTF_RESET); + } + if (result != 0) { + printf(PRINTF_RED "ERROR!" PRINTF_RESET); + } + printf("\n"); + } +} + +int main(int argument_count, char **arguments) { + os_enable_console_colors(); + Arena arena = {}; + Arena scratch = {}; + arena_init(&arena, "Pernament arena"_s); + arena_init(&scratch, "Pernament scratch arena"_s); + + Array args = {&scratch}; + for (int i = 1; i < argument_count; i += 1) { + String arg = string_from_cstring(arguments[i]); + args.add(arg); + } + + if (!args.len) { + printf("Please specify a file to compile!"); + return 0; + } + + For(args) { + if (it == "-testing"_s) { + Scoped_Arena _scope(&scratch); + Array examples = os_list_dir(&scratch, &scratch, "examples"_s); + Array tests = os_list_dir(&scratch, &scratch, "tests"_s); + For(examples) { + if (it.is_directory) continue; + String filename = string_skip_to_last_slash(it.relative_path); + if (filename.len && filename.str[0] == '_') continue; + compile_file(&arena, it.absolute_path, COMPILE_AND_RUN | COMPILE_TESTING); + } + For(tests) { + if (it.is_directory) continue; + compile_file(&arena, it.absolute_path, COMPILE_AND_RUN | COMPILE_TESTING); + } + } + + else { + String program_name = string_from_cstring(arguments[1]); + compile_file(&arena, program_name, COMPILE_PRINT_STATS | DONT_USE_C_COMPILER); + } + } + printf("End of program\n"); + if (IsDebuggerPresent()) { + Breakpoint; + } + + return 0; +} diff --git a/core_parsing.cpp b/src/language/core_parsing.cpp similarity index 97% rename from core_parsing.cpp rename to src/language/core_parsing.cpp index e8682cc..df402c1 100644 --- a/core_parsing.cpp +++ b/src/language/core_parsing.cpp @@ -1,1109 +1,1109 @@ -CORE_Static Ast_Decl *parse_decl(B32 is_global); - -static Core_Message *core_add_message(Core_Message_Kind kind, String string, Token *pos1, Token *pos2 = 0, int line = -1, const char *file = 0) { - if (kind == CORE_ERROR) pctx->errors_occured += 1; - if (kind == CORE_WARNING) pctx->warnings_occured += 1; - Core_Message *message = allocate_struct(pctx->perm, Core_Message); - message->kind = kind; - message->string = string; - message->tokens[0] = pos1; - message->tokens[1] = pos2; - message->trace_line = line; - message->trace_file = (char *)file; - SLL_QUEUE_ADD(pctx->first_message, pctx->last_message, message); - return message; -} - -#define log_trace(...) core_log_trace(__LINE__, __FILE__, ##__VA_ARGS__) -static void core_log_trace(int line, const char *file, const char *str, ...) { - STRING_FMT(pctx->perm, str, string); - core_add_message(CORE_TRACE, string, 0, 0, line, file); -} - -#define PRINTF_GREEN "\033[32m" -#define PRINTF_RED "\033[31m" -#define PRINTF_RESET "\033[0m" - -String core_stringify_message(Core_Ctx *pctx, Allocator *allocator, Core_Message *msg, int color_codes_enabled = false) { - String_Builder &b = pctx->helper_builder; - - for (S64 i = 0; i < buff_cap(msg->tokens); i += 1) { - Token *it = msg->tokens[i]; - if (it) { - b.addf("\n%s(%d): ", it->file.str, (S64)it->line + 1); - if (msg->kind == CORE_ERROR) b.addf("error "); - else if (msg->kind == CORE_WARNING) b.addf("warning "); - else if (msg->kind == CORE_TRACE) b.addf("trace "); - else invalid_codepath; - } - } - - for (int i = 0; i < buff_cap(msg->tokens); i += 1) { - Token *it = msg->tokens[i]; - if (it) { - if (it->kind == TK_Error) { - b.addf("%Q | ", it->error_val); - } - } - } - - b.addf("%Q", msg->string); - - for (S64 i = 0; i < buff_cap(msg->tokens); i += 1) { - Token *token = msg->tokens[i]; - if (token) { - b.addf("\n"); - // Print from line begin to token - S64 i1 = token->str - token->line_begin; - b.addf("%.*s", i1, token->line_begin); - - // Print token part - if (color_codes_enabled) { - b.addf(PRINTF_RED "%.*s" PRINTF_RESET, (S64)token->len, token->str); - } - else { - b.addf("%.*s", (S64)token->len, token->str); - } - - // Print to end of line from token - S64 iend = 0; - U8 *pointer = token->str + token->len; - while (pointer[iend] != '\n' && pointer[iend] != 0) iend++; - b.addf("%.*s", iend, pointer); - } - } - - String result = string_flatten(allocator, &b); - return result; -} - -static void compiler_error(Token *token1, Token *token2, const char *str, ...) { - STRING_FMT(pctx->perm, str, string); - Core_Message *msg = core_add_message(CORE_ERROR, string, token1, token2); - if (pctx->debugger_break_on_compiler_error) { - String str = core_stringify_message(pctx, pctx->perm, msg, GLOBAL_EnabledConsoleColors); - printf("%s\n", str.str); // @! How to get rid of printf ? - fflush(stdout); - Breakpoint; - } -} - -CORE_Static void -compiler_error(Token *token, const char *str, ...) { - STRING_FMT(pctx->perm, str, string); - Core_Message *msg = core_add_message(CORE_ERROR, string, token); - if (pctx->debugger_break_on_compiler_error) { - String str = core_stringify_message(pctx, pctx->perm, msg, GLOBAL_EnabledConsoleColors); - printf("%s\n", str.str); // @! How to get rid of printf ? - fflush(stdout); - Breakpoint; - } -} - -CORE_Static Token * -token_get(S64 i = 0) { - i += pctx->token_iter; - if (i >= pctx->tokens.len) { - return &pctx->null_token; - } - Token *result = &pctx->tokens[i]; - return result; -} - -CORE_Static Token * -token_is_scope() { - Token *token = token_get(); - if (lex_is_scope(token)) return token; - return 0; -} - -CORE_Static Token * -token_next() { - Token *token = token_get(); - if (lex_is_scope(token)) pctx->indent = token->indent; - pctx->token_iter++; - return token; -} - -CORE_Static Token * -token_is(Token_Kind kind, S64 lookahead = 0) { - Token *token = token_get(lookahead); - if (token->kind == kind) { - return token; - } - return 0; -} - -CORE_Static Token * -token_is_keyword(Intern_String keyword, S64 lookahead = 0) { - Token *token = token_get(lookahead); - if (token->kind == TK_Keyword) { - if (keyword.str == token->intern_val.str) { - return token; - } - } - return 0; -} - -CORE_Static Token * -token_match_pound(Intern_String string) { - Token *token = token_get(); - if (token->kind == TK_Pound) { - if (token->intern_val == string) { - return token_next(); - } - } - return 0; -} - -CORE_Static Token * -token_match(Token_Kind kind) { - Token *token = token_get(); - if (token->kind == kind) { - return token_next(); - } - return 0; -} - -CORE_Static Token * -token_match(Token_Kind a, Token_Kind b) { - Token *ta = token_get(); - Token *tb = token_get(1); - if (ta->kind == a && tb->kind == b) { - token_next(); - token_next(); - return ta; - } - return 0; -} - -CORE_Static Token * -token_match_keyword(Intern_String string) { - Token *token = token_get(); - if (token->kind == TK_Keyword) { - if (string.str == token->intern_val.str) { - token = token_next(); - return token; - } - } - return 0; -} - -CORE_Static Token * -token_expect(Token_Kind kind) { - Token *token = token_get(); - if (token->kind == kind) return token_next(); - compiler_error(token, "Expected token of kind: [%s], got instead token of kind: [%s]", name(kind), name(token->kind)); - return 0; -} - -CORE_Static Ast_Expr * -parse_init_stmt(Ast_Expr *expr) { - Token *token = token_get(); - if (token->kind == TK_ColonAssign && expr->kind != AST_IDENT) - compiler_error(expr->pos, "Binding with [:=] to something that is not an identifier"); - - else if (token_is_assign(token)) { - token_next(); - Ast_Expr *value = parse_expr(); - Ast_Expr *result = 0; - if (token->kind == TK_ColonAssign) { - Ast_Atom *name = (Ast_Atom *)expr; - result = (Ast_Expr *)ast_var(token, 0, name->intern_val, value); - set_flag(result->flags, AST_EXPR); - } - else { - result = ast_expr_binary((Ast_Atom *)expr, value, token); - } - set_flag(result->flags, AST_STMT); - return result; - } - - return expr; -} - -CORE_Static Ast_Call * -parse_expr_call(Ast_Expr *left, Token_Kind close_kind) { - Scoped_Arena scratch(pctx->scratch); - Token *pos = token_get(); - Array exprs = {scratch.arena}; - - while (!token_is(close_kind)) { - Ast_Call_Item *item_comp = ast_new(Ast_Call_Item, AST_CALL_ITEM, token_get(), AST_EXPR); - item_comp->item = parse_expr(); - if (token_match(TK_Assign)) { - if (!is_flag_set(item_comp->item->flags, AST_ATOM)) { - compiler_error(item_comp->pos, "Invalid value specifier, it's required to be a simple identifier/index"); - } - - if (item_comp->item->kind != AST_IDENT) { - item_comp->index = item_comp->item; - set_flag(item_comp->call_flags, CALL_INDEX); - } - else { - item_comp->name = (Ast_Atom *)item_comp->item; - set_flag(item_comp->call_flags, CALL_NAME); - } - - item_comp->item = parse_expr(); - } - - if (close_kind == TK_OpenParen && is_flag_set(item_comp->call_flags, CALL_INDEX)) - compiler_error(item_comp->pos, "Lambda calls can't have indexed arguments"); - - exprs.add(item_comp); - if (!token_match(TK_Comma)) { - break; - } - } - token_expect(close_kind); - - Ast_Call *result = ast_call(pos, left, exprs); - return result; -} - -CORE_Static Ast_Expr * -parse_optional_type() { - Ast_Expr *result = 0; - if (token_match(TK_Colon)) { - result = parse_expr(); - } - return result; -} - -CORE_Static Ast_Scope *parse_stmt_scope(Ast_Scope *scope_defined_outside = 0); - -CORE_Static Ast * -parse_stmt() { - Token *token = token_get(); - Scoped_Arena scratch(pctx->scratch); - - if (token_match_keyword(pctx->keyword_return)) { - Ast_Expr *expr = 0; - if (!token_is_scope()) expr = parse_expr(); - return ast_return(token, expr); - } - - else if (token_match_keyword(pctx->keyword_continue)) { - return ast_continue(token); - } - - else if (token_match_keyword(pctx->intern_compiler_breakpoint)) { - return ast_compiler_breakpoint(token); - } - - else if (token_match_keyword(pctx->keyword_break)) { - return ast_break(token); - } - - else if (token_match_keyword(pctx->keyword_pass)) { - return ast_pass(token); - } - - else if (token_match_keyword(pctx->keyword_switch)) { - Ast_Switch *result = ast_new(Ast_Switch, AST_SWITCH, token, AST_STMT); - result->value = parse_expr(); - result->cases = {scratch.arena}; - - token_expect(OPEN_SCOPE); - do { - if (token_match_keyword(pctx->keyword_default)) { - result->default_scope = parse_stmt_scope(); - continue; - } - - Ast_Switch_Case *switch_case = ast_new(Ast_Switch_Case, AST_SWITCH_CASE, token_get(), AST_STMT); - if (token_match_pound(pctx->intern("fallthrough"_s))) - switch_case->fallthrough = true; - - switch_case->labels = {scratch.arena}; - do { - switch_case->labels.add(parse_expr()); - } while (token_match(TK_Comma)); - switch_case->labels = switch_case->labels.tight_copy(pctx->perm); - - switch_case->scope = parse_stmt_scope(); - result->cases.add(switch_case); - } while (token_match(SAME_SCOPE)); - token_expect(CLOSE_SCOPE); - result->cases = result->cases.tight_copy(pctx->perm); - - return result; - } - - else if (token_match_keyword(pctx->keyword_assert)) { - token_expect(TK_OpenParen); - Ast_Expr *expr = parse_expr(); - Intern_String message = {}; - if (token_match(TK_Comma)) { - Token *t = token_expect(TK_StringLit); - message = t->intern_val; - } - token_expect(TK_CloseParen); - return ast_runtime_assert(token, expr, message); - } - - else if (token_match_pound(pctx->keyword_assert)) { - token_expect(TK_OpenParen); - Ast_Expr *expr = parse_expr(); - Intern_String message = {}; - if (token_match(TK_Comma)) { - Token *t = token_expect(TK_StringLit); - message = t->intern_val; - } - token_expect(TK_CloseParen); - return ast_constant_assert(token, expr, message); - } - - else if (token_match_keyword(pctx->keyword_for)) { - Ast_Scope *for_scope = begin_stmt_scope(scratch.arena, token_get()); - Ast_Expr *init = 0; - Ast_Expr *cond = 0; - Ast_Expr *iter = 0; - - if (!token_is(OPEN_SCOPE)) { - if (!token_is(TK_Comma)) { - Ast_Expr *expr_first = parse_expr(); - init = parse_init_stmt(expr_first); - } - - if (token_match(TK_Comma)) { - if (!token_is(TK_Comma)) cond = parse_expr(); - if (token_match(TK_Comma)) { - iter = parse_expr(); - iter = parse_init_stmt(iter); - } - } - } - - parse_stmt_scope(for_scope); - finalize_stmt_scope(for_scope); - return ast_for(token, init, cond, iter, for_scope); - } - - else if (token_match_keyword(pctx->keyword_if)) { - Array if_nodes = {scratch.arena}; - Ast_Expr *expr = parse_expr(); - Ast_Expr *init_val = parse_init_stmt(expr); - if (init_val != expr) { - if (token_match(TK_Comma)) expr = parse_expr(); - else expr = 0; - } - if (init_val == expr) init_val = 0; - - Ast_Scope *if_block = parse_stmt_scope(); - Ast_If_Node *if_node = ast_if_node(token, init_val, expr, if_block); - if_nodes.add(if_node); - - while (token_is(SAME_SCOPE) && (token_is_keyword(pctx->keyword_elif, 1) || (token_is_keyword(pctx->keyword_else, 1)))) { - token_next(); - token = token_get(); - if (token_match_keyword(pctx->keyword_elif)) { - assert(token->intern_val == pctx->keyword_elif); - Ast_Expr *expr = parse_expr(); - Ast_Scope *else_if_block = parse_stmt_scope(); - Ast_If_Node *if_node = ast_if_node(token, 0, expr, else_if_block); - if_nodes.add(if_node); - } - else { - token_match_keyword(pctx->keyword_else); - assert(token->intern_val == pctx->keyword_else); - Ast_Scope *else_block = parse_stmt_scope(); - Ast_If_Node *if_node = ast_if_node(token, 0, 0, else_block); - if_nodes.add(if_node); - break; - } - } - return ast_if(token, if_nodes); - } - - else if (token_match_keyword(pctx->keyword_defer)) { - Ast_Scope *s = parse_stmt_scope(); - return ast_defer(token, s); - } - - else if (token_match_keyword(pctx->keyword_goto)) { - Token *ident = token_expect(TK_Identifier); - return ast_goto(token, ident->intern_val); - } - - // Label - else if (token_match(TK_Colon)) { - Token *ident = token_expect(TK_Identifier); - Token *enable_goto_token = token_match(TK_Colon); - bool enable_goto = enable_goto_token ? 1 : 0; - Token *breakpoint = token_match_pound(pctx->intern_compiler_breakpoint); - - Ast_Scope *s = 0; - if (token_is(OPEN_SCOPE)) s = parse_stmt_scope(); - - Ast_Label *result = ast_label(token, ident->intern_val, s, enable_goto); - if (breakpoint) set_flag(result->flags, AST_COMPILER_BREAKPOINT); - return result; - } - - // Var unpack - else if (token_is(TK_Identifier) && token_is(TK_Comma, 1)) { - Array decls = {scratch.arena}; - do { - Token *name = token_match(TK_Identifier); - Ast_Decl *decl = ast_var(name, 0, name->intern_val, 0); - decls.add(decl); - } while (token_match(TK_Comma)); - - token_expect(TK_ColonAssign); - Ast_Expr *expr = parse_expr(); - Ast_Var_Unpack *vars = ast_var_unpack(token, decls, expr); - return vars; - } - - // Declaration or init stmt - else { - Ast *result = parse_decl(false); - if (result && result->kind != AST_VAR && result->kind != AST_CONST) { - compiler_error(token, "Invalid statement construct"); - } - if (!result) { - result = parse_expr(); - result = parse_init_stmt((Ast_Expr *)result); - } - if (result) { - result->flags = set_flag(result->flags, AST_STMT); - return result; - } - else { - compiler_error(token, "Unexpected token [%s] while parsing statement", name(token->kind)); - return 0; - } - } -} - -CORE_Static Ast_Scope * -parse_stmt_scope(Ast_Scope *scope_defined_outside) { - Scoped_Arena scratch(pctx->scratch); - Ast_Scope *scope = scope_defined_outside; - if (!scope_defined_outside) scope = begin_stmt_scope(scratch.arena, token_get()); - - if (token_expect(OPEN_SCOPE)) { // @todo: Fix error message here, it doesn't show proper token context - do { - Ast *stmt = parse_stmt(); - scope->stmts.add(stmt); - if (stmt->kind == AST_DEFER) scope->defers.add((Ast_Defer *)stmt); - } while (token_match(SAME_SCOPE)); - token_expect(CLOSE_SCOPE); - } - - if (!scope_defined_outside) finalize_stmt_scope(scope); - return scope; -} - -CORE_Static Array -parse_parameter_list(Arena *arena) { - Array params = {arena}; - if (!token_is(TK_CloseParen)) { - for (;;) { - Token *name = token_next(); - if (name->kind == TK_ThreeDots) { - Ast_Decl *param = ast_new(Ast_Decl, AST_VARGS_LAMBDA_PARAM, name, AST_DECL); - param->name = pctx->intern("..."_s); - params.add(param); - break; - } - else if (name->kind == TK_Identifier || name->kind == TK_Polymorph) { - token_expect(TK_Colon); - - Ast_Decl *param = ast_new(Ast_Decl, AST_VAR, name, AST_DECL); - param->name = name->intern_val; - param->unique_name = name->intern_val; - param->typespec = parse_expr(); - set_flag_typespec(param->typespec); - if (is_typespec_polymorphic(param->typespec)) { - set_flag(param->flags, AST_TYPE_POLYMORPH); - } - if (name->kind == TK_Polymorph) { - set_flag(param->flags, AST_IDENT_POLYMORPH); - } - - if (token_match(TK_Assign)) { - param->expr = parse_expr(); - } - - params.add(param); - } - else compiler_error(name, "Expected [Identifier] or [..] when parsing lambda arguments"); - - if (!token_match(TK_Comma)) - break; - } - } - token_expect(TK_CloseParen); - return params; -} - -CORE_Static Ast_Lambda * -parse_lambda(Token *token) { - Scoped_Arena scratch(pctx->scratch); - - Array params = parse_parameter_list(scratch.arena); - Ast_Expr *ret = parse_optional_type(); - Ast_Scope *scope = token_is(OPEN_SCOPE) ? parse_stmt_scope() : 0; - Ast_Lambda *result = ast_lambda(token, params, ret, scope); - return result; -} - -//----------------------------------------------------------------------------- -// Pratt expression parser -// Based on this really good article: https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html -//----------------------------------------------------------------------------- -struct Binding_Power { - S64 left; - S64 right; -}; -enum Binding { Binding_Prefix, - Binding_Infix, - Binding_Postfix }; - -CORE_Static Binding_Power -binding_power(Binding binding, Token_Kind kind) { - if (binding == Binding_Prefix) goto Prefix; - if (binding == Binding_Infix) goto Infix; - if (binding == Binding_Postfix) goto Postfix; - else invalid_codepath; - -Prefix: - switch (kind) { - case TK_OpenBracket: - return {-2, 22}; - case TK_Increment: - case TK_Decrement: - case TK_Pointer: - case TK_Dereference: - case TK_Keyword: - case TK_OpenParen: - case TK_OpenBrace: - case TK_Sub: - case TK_Add: - case TK_Neg: - case TK_Not: - return {-2, 20}; - default: return {-1, -1}; - } -Infix: - switch (kind) { - case TK_Or: - return {9, 10}; - case TK_And: - return {11, 12}; - case TK_Equals: - case TK_NotEquals: - case TK_GreaterThen: - case TK_GreaterThenOrEqual: - case TK_LesserThen: - case TK_LesserThenOrEqual: - return {13, 14}; - case TK_Sub: - case TK_Add: - case TK_BitOr: - case TK_BitXor: - return {15, 16}; - case TK_RightShift: - case TK_LeftShift: - case TK_BitAnd: - case TK_Mul: - case TK_Div: - case TK_Mod: - return {17, 18}; - case TK_Dot: - return {31, 30}; - case TK_Arrow: - return {20, 21}; - default: return {}; - } -Postfix: - switch (kind) { - case TK_Increment: - case TK_Decrement: - case TK_OpenBracket: - case TK_OpenParen: - case TK_OpenBrace: - return {21, -2}; - default: return {-1, -1}; - } -} - -CORE_Static Ast_Expr * -parse_expr(S64 min_bp) { - Ast_Expr *left = 0; - Token *token = token_next(); - Binding_Power prefix_bp = binding_power(Binding_Prefix, token->kind); - - // parse prefix expression - switch (token->kind) { - case TK_StringLit: left = ast_str(token, token->intern_val); break; - case TK_Identifier: left = ast_ident(token, token->intern_val); break; - case TK_Polymorph: { - left = ast_ident(token, token->intern_val); - set_flag(left->flags, AST_POLYMORPH); - } break; - case TK_Integer: left = ast_int(token, token->int_val); break; - case TK_UnicodeLit: left = ast_int(token, token->unicode); break; - case TK_Float: left = ast_float(token, token->f64_val); break; - case TK_Sub: left = ast_expr_unary(token, TK_Sub, parse_expr(prefix_bp.right)); break; - case TK_Add: left = ast_expr_unary(token, TK_Add, parse_expr(prefix_bp.right)); break; - case TK_Not: left = ast_expr_unary(token, TK_Not, parse_expr(prefix_bp.right)); break; - case TK_Neg: left = ast_expr_unary(token, TK_Neg, parse_expr(prefix_bp.right)); break; - case TK_Increment: left = ast_expr_unary(token, TK_Increment, parse_expr(prefix_bp.right)); break; - case TK_Decrement: left = ast_expr_unary(token, TK_Decrement, parse_expr(prefix_bp.right)); break; - case TK_Dereference: left = ast_expr_unary(token, TK_Dereference, parse_expr(prefix_bp.right)); break; - - // Pointer -> *int, Deref -> *var - case TK_Pointer: { - left = ast_expr_unary(token, TK_Pointer, parse_expr(prefix_bp.right)); - auto unary = (Ast_Unary *)left; - } break; - - // Array subscript -> [32]int - case TK_OpenBracket: { - Ast_Expr *expr = 0; - if (!token_is(TK_CloseBracket)) - expr = parse_expr(0); - - Ast_Array *result = ast_array(token, expr); - token_expect(TK_CloseBracket); - result->base = parse_expr(prefix_bp.right); - left = result; - } break; - - // Compound expression - case TK_OpenBrace: { - left = parse_expr_call(0, TK_CloseBrace); - left->kind = AST_COMPOUND; - } break; - - // True, False - case TK_Keyword: { - if (token->intern_val == pctx->keyword_true) - left = ast_bool(token, 1); - else if (token->intern_val == pctx->keyword_false) - left = ast_bool(token, 0); - else compiler_error(token, "Unexpected keyword: [%s]", token->intern_val.str); - } break; - - case TK_OpenParen: { - if (token_is(TK_CloseParen) || (token_is(TK_Identifier) && token_is(TK_Colon, 1)) || (token_is(TK_Polymorph) && token_is(TK_Colon, 1)) || token_is(TK_ThreeDots)) - left = parse_lambda(token); - else { - left = parse_expr(0); - token_expect(TK_CloseParen); - } - } break; - default: compiler_error(token, "Unexpected token of kind: [%s] in expression", name(token->kind)); return 0; - } - - for (;;) { - token = token_get(); - - // lets say [+] is left:1, right:2 and we parse 2+3+4 - // We pass min_bp of 2 to the next recursion - // in recursion we check if left(1) > min_bp(2) - // it's not so we don't recurse - we break - // We do standard do the for loop instead - - Binding_Power postfix_bp = binding_power(Binding_Postfix, token->kind); - Binding_Power infix_bp = binding_power(Binding_Infix, token->kind); - - // parse postfix expression - if (postfix_bp.left > min_bp) { - token_next(); - switch (token->kind) { - case TK_OpenBracket: { - Ast_Expr *index = parse_expr(0); - token_expect(TK_CloseBracket); - left = ast_expr_index(token, left, index); - } break; - case TK_OpenBrace: { - left = parse_expr_call(left, TK_CloseBrace); - left->kind = AST_COMPOUND; - } break; - case TK_OpenParen: { - left = parse_expr_call(left, TK_CloseParen); - } break; - default: { - assert(token->kind == TK_Increment || token->kind == TK_Decrement); - if (token->kind == TK_Increment) token->kind = TK_PostIncrement; - else if (token->kind == TK_Decrement) token->kind = TK_PostDecrement; - left = ast_expr_unary(token, token->kind, left); - } - } - } - - // parse infix expression - else if (infix_bp.left > min_bp) { - token = token_next(); - Ast_Expr *right = parse_expr(infix_bp.right); - left = ast_expr_binary(left, right, token); - } - - else break; - } - return left; -} - -CORE_Static Ast_Expr * -parse_assign_expr() { - Ast_Expr *result = 0; - if (token_match(TK_Assign)) result = parse_expr(); - return result; -} - -CORE_Static Ast_Decl * -parse_struct(Token *pos, Ast_Kind kind) { - Scoped_Arena scratch(pctx->scratch); - - Array params = {}; - if (token_match(TK_OpenParen)) { - params = parse_parameter_list(scratch.arena); - For(params) { - if (!is_flag_set(it->flags, AST_POLYMORPH)) { - compiler_error(it->pos, "All struct arguments are required to be polymorphic"); - } - } - params = params.tight_copy(pctx->perm); - } - - Ast_Scope *scope = begin_decl_scope(scratch.arena, token_get()); - token_match(OPEN_SCOPE); - do { - Token *token = token_expect(TK_Identifier); - token_expect(TK_Colon); - - Ast_Expr *typespec = parse_expr(); - set_flag_typespec(typespec); - - Ast_Decl *decl = ast_var(token, typespec, token->intern_val, 0); - set_flag(decl->flags, AST_AGGREGATE_CHILD); - add(pctx->perm, &scope->decls, decl); - - } while (token_match(SAME_SCOPE)); - token_expect(CLOSE_SCOPE); - - finalize_decl_scope(scope); - Ast_Decl *result = ast_struct(pos, scope, kind, params); - return result; -} - -CORE_Static Ast_Decl * -parse_enum(Token *pos) { - Scoped_Arena scratch(pctx->scratch); - Ast_Expr *typespec = parse_optional_type(); - Token *flag = token_match_pound(pctx->intern_flag); - - token_match(OPEN_SCOPE); - Ast_Scope *scope = begin_decl_scope(scratch.arena, token_get()); - do { - Token *name = token_expect(TK_Identifier); - Ast_Expr *value = 0; - if (token_match(TK_DoubleColon)) value = parse_expr(); - Ast_Decl *member = ast_const(name, name->intern_val, value); - add(pctx->perm, &scope->decls, member); - } while (token_match(SAME_SCOPE)); - finalize_decl_scope(scope); - token_expect(CLOSE_SCOPE); - - Ast_Decl *result = ast_enum(pos, typespec, scope); - if (flag) set_flag(result->flags, AST_FLAG); - return result; -} - -CORE_Static void -add_implicit_import(Ast_Scope *scope, Ast_Scope *to_add) { - B32 found = false; - For(scope->implicit_imports) { - if (it == to_add) { - found = true; - break; - } - } - if (!found) { - add(pctx->perm, &scope->implicit_imports, to_add); - } -} - -enum { GLOBAL_IMPLICIT_LOAD = 1 }; - -CORE_Static Ast_File * -register_ast_file(Token *pos, String absolute_file_path, Ast_Module *module, B32 global_implicit_load) { - Ast_File *file = 0; - - For(pctx->files) { - if (string_compare(it->absolute_file_path, absolute_file_path)) { - if (module == it->module) { - log_trace("%Q :: Returning registered file: %Q\n", module->absolute_file_path, absolute_file_path); - file = it; - break; - } - - compiler_error(it->pos, pos, "This file is already loaded by module: %Q, try importing that module to get access to it", module->absolute_file_path); - } - } - - if (!file) { - log_trace("%Q :: Registering file: %Q\n", module->absolute_file_path, absolute_file_path); - AST_NEW(File, FILE, 0, 0); - file = result; - file->absolute_file_path = absolute_file_path; - file->absolute_base_folder = string_copy(pctx->perm, string_chop_last_slash(file->absolute_file_path)); - file->module = module; - file->parent_scope = module; - file->file = file; // @warning: self referential! - file->pos = pos; - file->debug_name = string_skip_to_last_slash(absolute_file_path); - add(pctx->perm, &file->module->all_loaded_files, file); - file->scope_id = pctx->scope_ids++; - add(pctx->perm, &pctx->files, file); - } - - if (global_implicit_load) { - add_implicit_import(module, file); - } - - return file; -} - -CORE_Static Intern_String -preprocess_filename(Token *token_filename) { - Scoped_Arena _scope(pctx->scratch); - String filename = token_filename->intern_val.s; - Array replace = {pctx->scratch}; - replace.add({"$OS"_s, OS_NAME}); - replace.add({"$os"_s, OS_NAME_LOWER}); - String result0 = string_replace(pctx->scratch, pctx->scratch, filename, replace); - Intern_String result = pctx->intern(result0); - return result; -} - -CORE_Static Ast_File * -parse_load(B32 global_implicit_load) { - Token *file = token_expect(TK_StringLit); - Intern_String filename = preprocess_filename(file); - String absolute_path = string_fmt(pctx->perm, "%Q/%Q", pctx->currently_parsed_file->absolute_base_folder, filename); - Ast_File *result = register_ast_file(file, absolute_path, pctx->currently_parsed_file->module, global_implicit_load); - return result; -} - -CORE_Static Ast_Module *add_module(Token *pos, Intern_String filename, B32 command_line_module = false, bool string_only_module = false); -CORE_Static Ast_Module * -parse_import(B32 global_implicit_import) { - Token *file = token_expect(TK_StringLit); - Intern_String filename = preprocess_filename(file); - Ast_Module *result = add_module(file, filename); - if (global_implicit_import) { - add_implicit_import(pctx->currently_parsed_file->module, result); - } - return result; -} - -/* -Needs peeking only because I didn't want to duplicate code -for parsing statements and it makes code nicer. -Statements can have named syntax i := -*/ -CORE_Static Ast_Decl * -parse_decl(B32 is_global) { - Ast_Decl *result = 0; - if (is_global) { - token_match(SAME_SCOPE); - if (pctx->indent != 0) { - compiler_error(token_get(), "Top level declarations shouldn't be indented"); - } - } - - Ast_Flag flags = 0; - Token *tname = token_get(); - if (token_match(TK_Identifier, TK_DoubleColon)) { - for (;;) { - if (token_match_pound(pctx->intern_foreign)) set_flag(flags, AST_FOREIGN); - else if (token_match_pound(pctx->intern_strict)) set_flag(flags, AST_STRICT); - else if (token_match_pound(pctx->intern_compiler_breakpoint)) set_flag(flags, AST_COMPILER_BREAKPOINT); - else break; - } - - if (token_match_keyword(pctx->keyword_struct)) { - result = parse_struct(tname, AST_STRUCT); - } - - else if (token_match_keyword(pctx->keyword_union)) { - result = parse_struct(tname, AST_UNION); - } - - else if (token_match_keyword(pctx->keyword_enum)) { - result = parse_enum(tname); - } - - else if (token_match_pound(pctx->intern_load)) { - Ast_File *file = parse_load(false); - if (file->first_namespace_name.len == 0) file->first_namespace_name = tname->intern_val; - result = ast_namespace(tname, file, tname->intern_val); - } - - else if (token_match_pound(pctx->intern_import)) { - Ast_Module *module = parse_import(false); - if (module->first_namespace_name.len == 0) module->first_namespace_name = tname->intern_val; - result = ast_namespace(tname, module, tname->intern_val); - } - - else { - Ast_Expr *expr = parse_expr(); - result = ast_const(tname, tname->intern_val, expr); - - // @cleanup: consider simplifying lambdas, removing AST_LAMBDA_EXPR and - // implementing actual parse_lambda or something. Probably needs less - // ambigious syntax. - if (expr->kind == AST_LAMBDA_EXPR) { - auto lambda = (Ast_Lambda *)expr; - if (lambda->scope || is_flag_set(flags, AST_FOREIGN)) { - result->kind = AST_LAMBDA; - if (lambda->scope) lambda->scope->parent_decl = result; - if (is_flag_set(lambda->flags, AST_POLYMORPH)) { - set_flag(result->flags, AST_POLYMORPH); - set_flag(result->flags, AST_PARENT_POLYMORPH); - - int polymorph_count = 0; - For(lambda->args) { - if (it->flags & AST_POLYMORPH) { - polymorph_count += 1; - } - } - - result->polymorph_parameters.init(pctx->perm, polymorph_count); - For(lambda->args) { - if (it->flags & AST_POLYMORPH) { - result->polymorph_parameters.add(it); - } - } - } - if (is_flag_set(flags, AST_FOREIGN)) - set_flag(expr->flags, flags); - } - } - } - } - else if (token_match(TK_StringLit, TK_DoubleColon)) { - if (token_match_pound(pctx->intern_compiler_breakpoint)) set_flag(flags, AST_COMPILER_BREAKPOINT); - - // @cleanup: consider simplifying lambdas, removing AST_LAMBDA_EXPR and - // implementing actual parse_lambda or something. - Ast_Lambda *expr = (Ast_Lambda *)parse_expr(); - if (expr->kind != AST_LAMBDA_EXPR) { - compiler_error(tname, "Operator overload is required to be a lambda function"); - } - if (!expr->scope) { - compiler_error(tname, "Operator overload doesn't have body"); - } - Ast_Operator_Info *op_info = get_operator_info(tname->intern_val); - if (!op_info) { - compiler_error(tname, "This operator cannot be overloaded"); - } - - if (expr->args.len == 1) { - if (!op_info->valid_unary_expr) { - compiler_error(tname, "This operator cannot have a unary expression"); - } - } - else if (expr->args.len == 2) { - if (!op_info->valid_binary_expr) { - compiler_error(tname, "This operator cannot have a binary expression"); - } - } - else { - compiler_error(tname, "Invalid argument count for operator overload, unhandled operator"); - } - - result = ast_const(tname, tname->intern_val, expr); - result->overload_op_info = op_info; - result->kind = AST_LAMBDA; - result->flags = set_flag(result->flags, AST_OPERATOR_OVERLOAD); - } - - else if (token_match(TK_Identifier, TK_Colon)) { - Ast_Expr *typespec = parse_expr(); - set_flag_typespec(typespec); - - Ast_Expr *expr = parse_assign_expr(); - for (;;) { - if (token_match_pound(pctx->intern_foreign)) set_flag(flags, AST_FOREIGN); - else if (token_match_pound(pctx->intern_compiler_breakpoint)) set_flag(flags, AST_COMPILER_BREAKPOINT); - else break; - } - - result = ast_var(tname, typespec, tname->intern_val, expr); - } - - else if (token_match(TK_Identifier, TK_ColonAssign)) { - if (token_match_pound(pctx->intern_compiler_breakpoint)) set_flag(flags, AST_COMPILER_BREAKPOINT); - Ast_Expr *expr = parse_expr(); - result = ast_var(tname, 0, tname->intern_val, expr); - } - - else if (is_global && tname->kind != TK_End) { - compiler_error(tname, "Unexpected token: [%s] when parsing a declaration", name(tname->kind)); - } - - if (result) { - set_flag(result->flags, flags); - result->name = tname->intern_val; - result->unique_name = tname->intern_val; - } - - return result; -} - -CORE_Static void -parse_file(Ast_File *file) { - assert(file); - Scoped_Arena scratch(pctx->scratch); - - if (!file->filecontent.len) { - file->filecontent = os_read_file(pctx->perm, file->absolute_file_path); - if (file->filecontent.len == 0) { - compiler_error(file->pos, "Failed to open file \"%Q\"", file->absolute_file_path); - } - } - - pctx->currently_parsed_file = file; - pctx->currently_parsed_scope = file; - lex_restream(pctx, file->filecontent, file->absolute_file_path); - while (token_expect(SAME_SCOPE)) { - if (token_match_pound(pctx->intern_load)) { - parse_load(true); - continue; - } - else if (token_match_pound(pctx->intern_import)) { - parse_import(true); - continue; - } - else if (token_match_pound(pctx->intern_link)) { - Token *file = token_expect(TK_StringLit); - add(pctx->perm, &pctx->files_to_link, file); - continue; - } - - if (!file->pos) { - file->pos = token_get(); - } - - Ast_Decl *decl = parse_decl(true); - if (!decl) break; - - set_flag(decl->flags, AST_GLOBAL); - if (decl->kind == AST_STRUCT || decl->kind == AST_UNION) { - decl->type = pctx->type_type; - decl->type_val = type_incomplete(decl); - decl->state = DECL_RESOLVED; - } - - insert_into_scope(file, decl); - } - pctx->currently_parsed_scope = 0; - pctx->currently_parsed_file = 0; -} +CORE_Static Ast_Decl *parse_decl(B32 is_global); + +static Core_Message *core_add_message(Core_Message_Kind kind, String string, Token *pos1, Token *pos2 = 0, int line = -1, const char *file = 0) { + if (kind == CORE_ERROR) pctx->errors_occured += 1; + if (kind == CORE_WARNING) pctx->warnings_occured += 1; + Core_Message *message = allocate_struct(pctx->perm, Core_Message); + message->kind = kind; + message->string = string; + message->tokens[0] = pos1; + message->tokens[1] = pos2; + message->trace_line = line; + message->trace_file = (char *)file; + SLL_QUEUE_ADD(pctx->first_message, pctx->last_message, message); + return message; +} + +#define log_trace(...) core_log_trace(__LINE__, __FILE__, ##__VA_ARGS__) +static void core_log_trace(int line, const char *file, const char *str, ...) { + STRING_FMT(pctx->perm, str, string); + core_add_message(CORE_TRACE, string, 0, 0, line, file); +} + +#define PRINTF_GREEN "\033[32m" +#define PRINTF_RED "\033[31m" +#define PRINTF_RESET "\033[0m" + +String core_stringify_message(Core_Ctx *pctx, Allocator *allocator, Core_Message *msg, int color_codes_enabled = false) { + String_Builder &b = pctx->helper_builder; + + for (S64 i = 0; i < buff_cap(msg->tokens); i += 1) { + Token *it = msg->tokens[i]; + if (it) { + b.addf("\n%s(%d): ", it->file.str, (S64)it->line + 1); + if (msg->kind == CORE_ERROR) b.addf("error "); + else if (msg->kind == CORE_WARNING) b.addf("warning "); + else if (msg->kind == CORE_TRACE) b.addf("trace "); + else invalid_codepath; + } + } + + for (int i = 0; i < buff_cap(msg->tokens); i += 1) { + Token *it = msg->tokens[i]; + if (it) { + if (it->kind == TK_Error) { + b.addf("%Q | ", it->error_val); + } + } + } + + b.addf("%Q", msg->string); + + for (S64 i = 0; i < buff_cap(msg->tokens); i += 1) { + Token *token = msg->tokens[i]; + if (token) { + b.addf("\n"); + // Print from line begin to token + S64 i1 = token->str - token->line_begin; + b.addf("%.*s", i1, token->line_begin); + + // Print token part + if (color_codes_enabled) { + b.addf(PRINTF_RED "%.*s" PRINTF_RESET, (S64)token->len, token->str); + } + else { + b.addf("%.*s", (S64)token->len, token->str); + } + + // Print to end of line from token + S64 iend = 0; + U8 *pointer = token->str + token->len; + while (pointer[iend] != '\n' && pointer[iend] != 0) iend++; + b.addf("%.*s", iend, pointer); + } + } + + String result = string_flatten(allocator, &b); + return result; +} + +static void compiler_error(Token *token1, Token *token2, const char *str, ...) { + STRING_FMT(pctx->perm, str, string); + Core_Message *msg = core_add_message(CORE_ERROR, string, token1, token2); + if (pctx->debugger_break_on_compiler_error) { + String str = core_stringify_message(pctx, pctx->perm, msg, GLOBAL_EnabledConsoleColors); + printf("%s\n", str.str); // @! How to get rid of printf ? + fflush(stdout); + Breakpoint; + } +} + +CORE_Static void +compiler_error(Token *token, const char *str, ...) { + STRING_FMT(pctx->perm, str, string); + Core_Message *msg = core_add_message(CORE_ERROR, string, token); + if (pctx->debugger_break_on_compiler_error) { + String str = core_stringify_message(pctx, pctx->perm, msg, GLOBAL_EnabledConsoleColors); + printf("%s\n", str.str); // @! How to get rid of printf ? + fflush(stdout); + Breakpoint; + } +} + +CORE_Static Token * +token_get(S64 i = 0) { + i += pctx->token_iter; + if (i >= pctx->tokens.len) { + return &pctx->null_token; + } + Token *result = &pctx->tokens[i]; + return result; +} + +CORE_Static Token * +token_is_scope() { + Token *token = token_get(); + if (lex_is_scope(token)) return token; + return 0; +} + +CORE_Static Token * +token_next() { + Token *token = token_get(); + if (lex_is_scope(token)) pctx->indent = token->indent; + pctx->token_iter++; + return token; +} + +CORE_Static Token * +token_is(Token_Kind kind, S64 lookahead = 0) { + Token *token = token_get(lookahead); + if (token->kind == kind) { + return token; + } + return 0; +} + +CORE_Static Token * +token_is_keyword(Intern_String keyword, S64 lookahead = 0) { + Token *token = token_get(lookahead); + if (token->kind == TK_Keyword) { + if (keyword.str == token->intern_val.str) { + return token; + } + } + return 0; +} + +CORE_Static Token * +token_match_pound(Intern_String string) { + Token *token = token_get(); + if (token->kind == TK_Pound) { + if (token->intern_val == string) { + return token_next(); + } + } + return 0; +} + +CORE_Static Token * +token_match(Token_Kind kind) { + Token *token = token_get(); + if (token->kind == kind) { + return token_next(); + } + return 0; +} + +CORE_Static Token * +token_match(Token_Kind a, Token_Kind b) { + Token *ta = token_get(); + Token *tb = token_get(1); + if (ta->kind == a && tb->kind == b) { + token_next(); + token_next(); + return ta; + } + return 0; +} + +CORE_Static Token * +token_match_keyword(Intern_String string) { + Token *token = token_get(); + if (token->kind == TK_Keyword) { + if (string.str == token->intern_val.str) { + token = token_next(); + return token; + } + } + return 0; +} + +CORE_Static Token * +token_expect(Token_Kind kind) { + Token *token = token_get(); + if (token->kind == kind) return token_next(); + compiler_error(token, "Expected token of kind: [%s], got instead token of kind: [%s]", name(kind), name(token->kind)); + return 0; +} + +CORE_Static Ast_Expr * +parse_init_stmt(Ast_Expr *expr) { + Token *token = token_get(); + if (token->kind == TK_ColonAssign && expr->kind != AST_IDENT) + compiler_error(expr->pos, "Binding with [:=] to something that is not an identifier"); + + else if (token_is_assign(token)) { + token_next(); + Ast_Expr *value = parse_expr(); + Ast_Expr *result = 0; + if (token->kind == TK_ColonAssign) { + Ast_Atom *name = (Ast_Atom *)expr; + result = (Ast_Expr *)ast_var(token, 0, name->intern_val, value); + set_flag(result->flags, AST_EXPR); + } + else { + result = ast_expr_binary((Ast_Atom *)expr, value, token); + } + set_flag(result->flags, AST_STMT); + return result; + } + + return expr; +} + +CORE_Static Ast_Call * +parse_expr_call(Ast_Expr *left, Token_Kind close_kind) { + Scoped_Arena scratch(pctx->scratch); + Token *pos = token_get(); + Array exprs = {scratch.arena}; + + while (!token_is(close_kind)) { + Ast_Call_Item *item_comp = ast_new(Ast_Call_Item, AST_CALL_ITEM, token_get(), AST_EXPR); + item_comp->item = parse_expr(); + if (token_match(TK_Assign)) { + if (!is_flag_set(item_comp->item->flags, AST_ATOM)) { + compiler_error(item_comp->pos, "Invalid value specifier, it's required to be a simple identifier/index"); + } + + if (item_comp->item->kind != AST_IDENT) { + item_comp->index = item_comp->item; + set_flag(item_comp->call_flags, CALL_INDEX); + } + else { + item_comp->name = (Ast_Atom *)item_comp->item; + set_flag(item_comp->call_flags, CALL_NAME); + } + + item_comp->item = parse_expr(); + } + + if (close_kind == TK_OpenParen && is_flag_set(item_comp->call_flags, CALL_INDEX)) + compiler_error(item_comp->pos, "Lambda calls can't have indexed arguments"); + + exprs.add(item_comp); + if (!token_match(TK_Comma)) { + break; + } + } + token_expect(close_kind); + + Ast_Call *result = ast_call(pos, left, exprs); + return result; +} + +CORE_Static Ast_Expr * +parse_optional_type() { + Ast_Expr *result = 0; + if (token_match(TK_Colon)) { + result = parse_expr(); + } + return result; +} + +CORE_Static Ast_Scope *parse_stmt_scope(Ast_Scope *scope_defined_outside = 0); + +CORE_Static Ast * +parse_stmt() { + Token *token = token_get(); + Scoped_Arena scratch(pctx->scratch); + + if (token_match_keyword(pctx->keyword_return)) { + Ast_Expr *expr = 0; + if (!token_is_scope()) expr = parse_expr(); + return ast_return(token, expr); + } + + else if (token_match_keyword(pctx->keyword_continue)) { + return ast_continue(token); + } + + else if (token_match_keyword(pctx->intern_compiler_breakpoint)) { + return ast_compiler_breakpoint(token); + } + + else if (token_match_keyword(pctx->keyword_break)) { + return ast_break(token); + } + + else if (token_match_keyword(pctx->keyword_pass)) { + return ast_pass(token); + } + + else if (token_match_keyword(pctx->keyword_switch)) { + Ast_Switch *result = ast_new(Ast_Switch, AST_SWITCH, token, AST_STMT); + result->value = parse_expr(); + result->cases = {scratch.arena}; + + token_expect(OPEN_SCOPE); + do { + if (token_match_keyword(pctx->keyword_default)) { + result->default_scope = parse_stmt_scope(); + continue; + } + + Ast_Switch_Case *switch_case = ast_new(Ast_Switch_Case, AST_SWITCH_CASE, token_get(), AST_STMT); + if (token_match_pound(pctx->intern("fallthrough"_s))) + switch_case->fallthrough = true; + + switch_case->labels = {scratch.arena}; + do { + switch_case->labels.add(parse_expr()); + } while (token_match(TK_Comma)); + switch_case->labels = switch_case->labels.tight_copy(pctx->perm); + + switch_case->scope = parse_stmt_scope(); + result->cases.add(switch_case); + } while (token_match(SAME_SCOPE)); + token_expect(CLOSE_SCOPE); + result->cases = result->cases.tight_copy(pctx->perm); + + return result; + } + + else if (token_match_keyword(pctx->keyword_assert)) { + token_expect(TK_OpenParen); + Ast_Expr *expr = parse_expr(); + Intern_String message = {}; + if (token_match(TK_Comma)) { + Token *t = token_expect(TK_StringLit); + message = t->intern_val; + } + token_expect(TK_CloseParen); + return ast_runtime_assert(token, expr, message); + } + + else if (token_match_pound(pctx->keyword_assert)) { + token_expect(TK_OpenParen); + Ast_Expr *expr = parse_expr(); + Intern_String message = {}; + if (token_match(TK_Comma)) { + Token *t = token_expect(TK_StringLit); + message = t->intern_val; + } + token_expect(TK_CloseParen); + return ast_constant_assert(token, expr, message); + } + + else if (token_match_keyword(pctx->keyword_for)) { + Ast_Scope *for_scope = begin_stmt_scope(scratch.arena, token_get()); + Ast_Expr *init = 0; + Ast_Expr *cond = 0; + Ast_Expr *iter = 0; + + if (!token_is(OPEN_SCOPE)) { + if (!token_is(TK_Comma)) { + Ast_Expr *expr_first = parse_expr(); + init = parse_init_stmt(expr_first); + } + + if (token_match(TK_Comma)) { + if (!token_is(TK_Comma)) cond = parse_expr(); + if (token_match(TK_Comma)) { + iter = parse_expr(); + iter = parse_init_stmt(iter); + } + } + } + + parse_stmt_scope(for_scope); + finalize_stmt_scope(for_scope); + return ast_for(token, init, cond, iter, for_scope); + } + + else if (token_match_keyword(pctx->keyword_if)) { + Array if_nodes = {scratch.arena}; + Ast_Expr *expr = parse_expr(); + Ast_Expr *init_val = parse_init_stmt(expr); + if (init_val != expr) { + if (token_match(TK_Comma)) expr = parse_expr(); + else expr = 0; + } + if (init_val == expr) init_val = 0; + + Ast_Scope *if_block = parse_stmt_scope(); + Ast_If_Node *if_node = ast_if_node(token, init_val, expr, if_block); + if_nodes.add(if_node); + + while (token_is(SAME_SCOPE) && (token_is_keyword(pctx->keyword_elif, 1) || (token_is_keyword(pctx->keyword_else, 1)))) { + token_next(); + token = token_get(); + if (token_match_keyword(pctx->keyword_elif)) { + assert(token->intern_val == pctx->keyword_elif); + Ast_Expr *expr = parse_expr(); + Ast_Scope *else_if_block = parse_stmt_scope(); + Ast_If_Node *if_node = ast_if_node(token, 0, expr, else_if_block); + if_nodes.add(if_node); + } + else { + token_match_keyword(pctx->keyword_else); + assert(token->intern_val == pctx->keyword_else); + Ast_Scope *else_block = parse_stmt_scope(); + Ast_If_Node *if_node = ast_if_node(token, 0, 0, else_block); + if_nodes.add(if_node); + break; + } + } + return ast_if(token, if_nodes); + } + + else if (token_match_keyword(pctx->keyword_defer)) { + Ast_Scope *s = parse_stmt_scope(); + return ast_defer(token, s); + } + + else if (token_match_keyword(pctx->keyword_goto)) { + Token *ident = token_expect(TK_Identifier); + return ast_goto(token, ident->intern_val); + } + + // Label + else if (token_match(TK_Colon)) { + Token *ident = token_expect(TK_Identifier); + Token *enable_goto_token = token_match(TK_Colon); + bool enable_goto = enable_goto_token ? 1 : 0; + Token *breakpoint = token_match_pound(pctx->intern_compiler_breakpoint); + + Ast_Scope *s = 0; + if (token_is(OPEN_SCOPE)) s = parse_stmt_scope(); + + Ast_Label *result = ast_label(token, ident->intern_val, s, enable_goto); + if (breakpoint) set_flag(result->flags, AST_COMPILER_BREAKPOINT); + return result; + } + + // Var unpack + else if (token_is(TK_Identifier) && token_is(TK_Comma, 1)) { + Array decls = {scratch.arena}; + do { + Token *name = token_match(TK_Identifier); + Ast_Decl *decl = ast_var(name, 0, name->intern_val, 0); + decls.add(decl); + } while (token_match(TK_Comma)); + + token_expect(TK_ColonAssign); + Ast_Expr *expr = parse_expr(); + Ast_Var_Unpack *vars = ast_var_unpack(token, decls, expr); + return vars; + } + + // Declaration or init stmt + else { + Ast *result = parse_decl(false); + if (result && result->kind != AST_VAR && result->kind != AST_CONST) { + compiler_error(token, "Invalid statement construct"); + } + if (!result) { + result = parse_expr(); + result = parse_init_stmt((Ast_Expr *)result); + } + if (result) { + result->flags = set_flag(result->flags, AST_STMT); + return result; + } + else { + compiler_error(token, "Unexpected token [%s] while parsing statement", name(token->kind)); + return 0; + } + } +} + +CORE_Static Ast_Scope * +parse_stmt_scope(Ast_Scope *scope_defined_outside) { + Scoped_Arena scratch(pctx->scratch); + Ast_Scope *scope = scope_defined_outside; + if (!scope_defined_outside) scope = begin_stmt_scope(scratch.arena, token_get()); + + if (token_expect(OPEN_SCOPE)) { // @todo: Fix error message here, it doesn't show proper token context + do { + Ast *stmt = parse_stmt(); + scope->stmts.add(stmt); + if (stmt->kind == AST_DEFER) scope->defers.add((Ast_Defer *)stmt); + } while (token_match(SAME_SCOPE)); + token_expect(CLOSE_SCOPE); + } + + if (!scope_defined_outside) finalize_stmt_scope(scope); + return scope; +} + +CORE_Static Array +parse_parameter_list(Arena *arena) { + Array params = {arena}; + if (!token_is(TK_CloseParen)) { + for (;;) { + Token *name = token_next(); + if (name->kind == TK_ThreeDots) { + Ast_Decl *param = ast_new(Ast_Decl, AST_VARGS_LAMBDA_PARAM, name, AST_DECL); + param->name = pctx->intern("..."_s); + params.add(param); + break; + } + else if (name->kind == TK_Identifier || name->kind == TK_Polymorph) { + token_expect(TK_Colon); + + Ast_Decl *param = ast_new(Ast_Decl, AST_VAR, name, AST_DECL); + param->name = name->intern_val; + param->unique_name = name->intern_val; + param->typespec = parse_expr(); + set_flag_typespec(param->typespec); + if (is_typespec_polymorphic(param->typespec)) { + set_flag(param->flags, AST_TYPE_POLYMORPH); + } + if (name->kind == TK_Polymorph) { + set_flag(param->flags, AST_IDENT_POLYMORPH); + } + + if (token_match(TK_Assign)) { + param->expr = parse_expr(); + } + + params.add(param); + } + else compiler_error(name, "Expected [Identifier] or [..] when parsing lambda arguments"); + + if (!token_match(TK_Comma)) + break; + } + } + token_expect(TK_CloseParen); + return params; +} + +CORE_Static Ast_Lambda * +parse_lambda(Token *token) { + Scoped_Arena scratch(pctx->scratch); + + Array params = parse_parameter_list(scratch.arena); + Ast_Expr *ret = parse_optional_type(); + Ast_Scope *scope = token_is(OPEN_SCOPE) ? parse_stmt_scope() : 0; + Ast_Lambda *result = ast_lambda(token, params, ret, scope); + return result; +} + +//----------------------------------------------------------------------------- +// Pratt expression parser +// Based on this really good article: https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html +//----------------------------------------------------------------------------- +struct Binding_Power { + S64 left; + S64 right; +}; +enum Binding { Binding_Prefix, + Binding_Infix, + Binding_Postfix }; + +CORE_Static Binding_Power +binding_power(Binding binding, Token_Kind kind) { + if (binding == Binding_Prefix) goto Prefix; + if (binding == Binding_Infix) goto Infix; + if (binding == Binding_Postfix) goto Postfix; + else invalid_codepath; + +Prefix: + switch (kind) { + case TK_OpenBracket: + return {-2, 22}; + case TK_Increment: + case TK_Decrement: + case TK_Pointer: + case TK_Dereference: + case TK_Keyword: + case TK_OpenParen: + case TK_OpenBrace: + case TK_Sub: + case TK_Add: + case TK_Neg: + case TK_Not: + return {-2, 20}; + default: return {-1, -1}; + } +Infix: + switch (kind) { + case TK_Or: + return {9, 10}; + case TK_And: + return {11, 12}; + case TK_Equals: + case TK_NotEquals: + case TK_GreaterThen: + case TK_GreaterThenOrEqual: + case TK_LesserThen: + case TK_LesserThenOrEqual: + return {13, 14}; + case TK_Sub: + case TK_Add: + case TK_BitOr: + case TK_BitXor: + return {15, 16}; + case TK_RightShift: + case TK_LeftShift: + case TK_BitAnd: + case TK_Mul: + case TK_Div: + case TK_Mod: + return {17, 18}; + case TK_Dot: + return {31, 30}; + case TK_Arrow: + return {20, 21}; + default: return {}; + } +Postfix: + switch (kind) { + case TK_Increment: + case TK_Decrement: + case TK_OpenBracket: + case TK_OpenParen: + case TK_OpenBrace: + return {21, -2}; + default: return {-1, -1}; + } +} + +CORE_Static Ast_Expr * +parse_expr(S64 min_bp) { + Ast_Expr *left = 0; + Token *token = token_next(); + Binding_Power prefix_bp = binding_power(Binding_Prefix, token->kind); + + // parse prefix expression + switch (token->kind) { + case TK_StringLit: left = ast_str(token, token->intern_val); break; + case TK_Identifier: left = ast_ident(token, token->intern_val); break; + case TK_Polymorph: { + left = ast_ident(token, token->intern_val); + set_flag(left->flags, AST_POLYMORPH); + } break; + case TK_Integer: left = ast_int(token, token->int_val); break; + case TK_UnicodeLit: left = ast_int(token, token->unicode); break; + case TK_Float: left = ast_float(token, token->f64_val); break; + case TK_Sub: left = ast_expr_unary(token, TK_Sub, parse_expr(prefix_bp.right)); break; + case TK_Add: left = ast_expr_unary(token, TK_Add, parse_expr(prefix_bp.right)); break; + case TK_Not: left = ast_expr_unary(token, TK_Not, parse_expr(prefix_bp.right)); break; + case TK_Neg: left = ast_expr_unary(token, TK_Neg, parse_expr(prefix_bp.right)); break; + case TK_Increment: left = ast_expr_unary(token, TK_Increment, parse_expr(prefix_bp.right)); break; + case TK_Decrement: left = ast_expr_unary(token, TK_Decrement, parse_expr(prefix_bp.right)); break; + case TK_Dereference: left = ast_expr_unary(token, TK_Dereference, parse_expr(prefix_bp.right)); break; + + // Pointer -> *int, Deref -> *var + case TK_Pointer: { + left = ast_expr_unary(token, TK_Pointer, parse_expr(prefix_bp.right)); + auto unary = (Ast_Unary *)left; + } break; + + // Array subscript -> [32]int + case TK_OpenBracket: { + Ast_Expr *expr = 0; + if (!token_is(TK_CloseBracket)) + expr = parse_expr(0); + + Ast_Array *result = ast_array(token, expr); + token_expect(TK_CloseBracket); + result->base = parse_expr(prefix_bp.right); + left = result; + } break; + + // Compound expression + case TK_OpenBrace: { + left = parse_expr_call(0, TK_CloseBrace); + left->kind = AST_COMPOUND; + } break; + + // True, False + case TK_Keyword: { + if (token->intern_val == pctx->keyword_true) + left = ast_bool(token, 1); + else if (token->intern_val == pctx->keyword_false) + left = ast_bool(token, 0); + else compiler_error(token, "Unexpected keyword: [%s]", token->intern_val.str); + } break; + + case TK_OpenParen: { + if (token_is(TK_CloseParen) || (token_is(TK_Identifier) && token_is(TK_Colon, 1)) || (token_is(TK_Polymorph) && token_is(TK_Colon, 1)) || token_is(TK_ThreeDots)) + left = parse_lambda(token); + else { + left = parse_expr(0); + token_expect(TK_CloseParen); + } + } break; + default: compiler_error(token, "Unexpected token of kind: [%s] in expression", name(token->kind)); return 0; + } + + for (;;) { + token = token_get(); + + // lets say [+] is left:1, right:2 and we parse 2+3+4 + // We pass min_bp of 2 to the next recursion + // in recursion we check if left(1) > min_bp(2) + // it's not so we don't recurse - we break + // We do standard do the for loop instead + + Binding_Power postfix_bp = binding_power(Binding_Postfix, token->kind); + Binding_Power infix_bp = binding_power(Binding_Infix, token->kind); + + // parse postfix expression + if (postfix_bp.left > min_bp) { + token_next(); + switch (token->kind) { + case TK_OpenBracket: { + Ast_Expr *index = parse_expr(0); + token_expect(TK_CloseBracket); + left = ast_expr_index(token, left, index); + } break; + case TK_OpenBrace: { + left = parse_expr_call(left, TK_CloseBrace); + left->kind = AST_COMPOUND; + } break; + case TK_OpenParen: { + left = parse_expr_call(left, TK_CloseParen); + } break; + default: { + assert(token->kind == TK_Increment || token->kind == TK_Decrement); + if (token->kind == TK_Increment) token->kind = TK_PostIncrement; + else if (token->kind == TK_Decrement) token->kind = TK_PostDecrement; + left = ast_expr_unary(token, token->kind, left); + } + } + } + + // parse infix expression + else if (infix_bp.left > min_bp) { + token = token_next(); + Ast_Expr *right = parse_expr(infix_bp.right); + left = ast_expr_binary(left, right, token); + } + + else break; + } + return left; +} + +CORE_Static Ast_Expr * +parse_assign_expr() { + Ast_Expr *result = 0; + if (token_match(TK_Assign)) result = parse_expr(); + return result; +} + +CORE_Static Ast_Decl * +parse_struct(Token *pos, Ast_Kind kind) { + Scoped_Arena scratch(pctx->scratch); + + Array params = {}; + if (token_match(TK_OpenParen)) { + params = parse_parameter_list(scratch.arena); + For(params) { + if (!is_flag_set(it->flags, AST_POLYMORPH)) { + compiler_error(it->pos, "All struct arguments are required to be polymorphic"); + } + } + params = params.tight_copy(pctx->perm); + } + + Ast_Scope *scope = begin_decl_scope(scratch.arena, token_get()); + token_match(OPEN_SCOPE); + do { + Token *token = token_expect(TK_Identifier); + token_expect(TK_Colon); + + Ast_Expr *typespec = parse_expr(); + set_flag_typespec(typespec); + + Ast_Decl *decl = ast_var(token, typespec, token->intern_val, 0); + set_flag(decl->flags, AST_AGGREGATE_CHILD); + add(pctx->perm, &scope->decls, decl); + + } while (token_match(SAME_SCOPE)); + token_expect(CLOSE_SCOPE); + + finalize_decl_scope(scope); + Ast_Decl *result = ast_struct(pos, scope, kind, params); + return result; +} + +CORE_Static Ast_Decl * +parse_enum(Token *pos) { + Scoped_Arena scratch(pctx->scratch); + Ast_Expr *typespec = parse_optional_type(); + Token *flag = token_match_pound(pctx->intern_flag); + + token_match(OPEN_SCOPE); + Ast_Scope *scope = begin_decl_scope(scratch.arena, token_get()); + do { + Token *name = token_expect(TK_Identifier); + Ast_Expr *value = 0; + if (token_match(TK_DoubleColon)) value = parse_expr(); + Ast_Decl *member = ast_const(name, name->intern_val, value); + add(pctx->perm, &scope->decls, member); + } while (token_match(SAME_SCOPE)); + finalize_decl_scope(scope); + token_expect(CLOSE_SCOPE); + + Ast_Decl *result = ast_enum(pos, typespec, scope); + if (flag) set_flag(result->flags, AST_FLAG); + return result; +} + +CORE_Static void +add_implicit_import(Ast_Scope *scope, Ast_Scope *to_add) { + B32 found = false; + For(scope->implicit_imports) { + if (it == to_add) { + found = true; + break; + } + } + if (!found) { + add(pctx->perm, &scope->implicit_imports, to_add); + } +} + +enum { GLOBAL_IMPLICIT_LOAD = 1 }; + +CORE_Static Ast_File * +register_ast_file(Token *pos, String absolute_file_path, Ast_Module *module, B32 global_implicit_load) { + Ast_File *file = 0; + + For(pctx->files) { + if (string_compare(it->absolute_file_path, absolute_file_path)) { + if (module == it->module) { + log_trace("%Q :: Returning registered file: %Q\n", module->absolute_file_path, absolute_file_path); + file = it; + break; + } + + compiler_error(it->pos, pos, "This file is already loaded by module: %Q, try importing that module to get access to it", module->absolute_file_path); + } + } + + if (!file) { + log_trace("%Q :: Registering file: %Q\n", module->absolute_file_path, absolute_file_path); + AST_NEW(File, FILE, 0, 0); + file = result; + file->absolute_file_path = absolute_file_path; + file->absolute_base_folder = string_copy(pctx->perm, string_chop_last_slash(file->absolute_file_path)); + file->module = module; + file->parent_scope = module; + file->file = file; // @warning: self referential! + file->pos = pos; + file->debug_name = string_skip_to_last_slash(absolute_file_path); + add(pctx->perm, &file->module->all_loaded_files, file); + file->scope_id = pctx->scope_ids++; + add(pctx->perm, &pctx->files, file); + } + + if (global_implicit_load) { + add_implicit_import(module, file); + } + + return file; +} + +CORE_Static Intern_String +preprocess_filename(Token *token_filename) { + Scoped_Arena _scope(pctx->scratch); + String filename = token_filename->intern_val.s; + Array replace = {pctx->scratch}; + replace.add({"$OS"_s, OS_NAME}); + replace.add({"$os"_s, OS_NAME_LOWER}); + String result0 = string_replace(pctx->scratch, pctx->scratch, filename, replace); + Intern_String result = pctx->intern(result0); + return result; +} + +CORE_Static Ast_File * +parse_load(B32 global_implicit_load) { + Token *file = token_expect(TK_StringLit); + Intern_String filename = preprocess_filename(file); + String absolute_path = string_fmt(pctx->perm, "%Q/%Q", pctx->currently_parsed_file->absolute_base_folder, filename); + Ast_File *result = register_ast_file(file, absolute_path, pctx->currently_parsed_file->module, global_implicit_load); + return result; +} + +CORE_Static Ast_Module *add_module(Token *pos, Intern_String filename, B32 command_line_module = false, bool string_only_module = false); +CORE_Static Ast_Module * +parse_import(B32 global_implicit_import) { + Token *file = token_expect(TK_StringLit); + Intern_String filename = preprocess_filename(file); + Ast_Module *result = add_module(file, filename); + if (global_implicit_import) { + add_implicit_import(pctx->currently_parsed_file->module, result); + } + return result; +} + +/* +Needs peeking only because I didn't want to duplicate code +for parsing statements and it makes code nicer. +Statements can have named syntax i := +*/ +CORE_Static Ast_Decl * +parse_decl(B32 is_global) { + Ast_Decl *result = 0; + if (is_global) { + token_match(SAME_SCOPE); + if (pctx->indent != 0) { + compiler_error(token_get(), "Top level declarations shouldn't be indented"); + } + } + + Ast_Flag flags = 0; + Token *tname = token_get(); + if (token_match(TK_Identifier, TK_DoubleColon)) { + for (;;) { + if (token_match_pound(pctx->intern_foreign)) set_flag(flags, AST_FOREIGN); + else if (token_match_pound(pctx->intern_strict)) set_flag(flags, AST_STRICT); + else if (token_match_pound(pctx->intern_compiler_breakpoint)) set_flag(flags, AST_COMPILER_BREAKPOINT); + else break; + } + + if (token_match_keyword(pctx->keyword_struct)) { + result = parse_struct(tname, AST_STRUCT); + } + + else if (token_match_keyword(pctx->keyword_union)) { + result = parse_struct(tname, AST_UNION); + } + + else if (token_match_keyword(pctx->keyword_enum)) { + result = parse_enum(tname); + } + + else if (token_match_pound(pctx->intern_load)) { + Ast_File *file = parse_load(false); + if (file->first_namespace_name.len == 0) file->first_namespace_name = tname->intern_val; + result = ast_namespace(tname, file, tname->intern_val); + } + + else if (token_match_pound(pctx->intern_import)) { + Ast_Module *module = parse_import(false); + if (module->first_namespace_name.len == 0) module->first_namespace_name = tname->intern_val; + result = ast_namespace(tname, module, tname->intern_val); + } + + else { + Ast_Expr *expr = parse_expr(); + result = ast_const(tname, tname->intern_val, expr); + + // @cleanup: consider simplifying lambdas, removing AST_LAMBDA_EXPR and + // implementing actual parse_lambda or something. Probably needs less + // ambigious syntax. + if (expr->kind == AST_LAMBDA_EXPR) { + auto lambda = (Ast_Lambda *)expr; + if (lambda->scope || is_flag_set(flags, AST_FOREIGN)) { + result->kind = AST_LAMBDA; + if (lambda->scope) lambda->scope->parent_decl = result; + if (is_flag_set(lambda->flags, AST_POLYMORPH)) { + set_flag(result->flags, AST_POLYMORPH); + set_flag(result->flags, AST_PARENT_POLYMORPH); + + int polymorph_count = 0; + For(lambda->args) { + if (it->flags & AST_POLYMORPH) { + polymorph_count += 1; + } + } + + result->polymorph_parameters.init(pctx->perm, polymorph_count); + For(lambda->args) { + if (it->flags & AST_POLYMORPH) { + result->polymorph_parameters.add(it); + } + } + } + if (is_flag_set(flags, AST_FOREIGN)) + set_flag(expr->flags, flags); + } + } + } + } + else if (token_match(TK_StringLit, TK_DoubleColon)) { + if (token_match_pound(pctx->intern_compiler_breakpoint)) set_flag(flags, AST_COMPILER_BREAKPOINT); + + // @cleanup: consider simplifying lambdas, removing AST_LAMBDA_EXPR and + // implementing actual parse_lambda or something. + Ast_Lambda *expr = (Ast_Lambda *)parse_expr(); + if (expr->kind != AST_LAMBDA_EXPR) { + compiler_error(tname, "Operator overload is required to be a lambda function"); + } + if (!expr->scope) { + compiler_error(tname, "Operator overload doesn't have body"); + } + Ast_Operator_Info *op_info = get_operator_info(tname->intern_val); + if (!op_info) { + compiler_error(tname, "This operator cannot be overloaded"); + } + + if (expr->args.len == 1) { + if (!op_info->valid_unary_expr) { + compiler_error(tname, "This operator cannot have a unary expression"); + } + } + else if (expr->args.len == 2) { + if (!op_info->valid_binary_expr) { + compiler_error(tname, "This operator cannot have a binary expression"); + } + } + else { + compiler_error(tname, "Invalid argument count for operator overload, unhandled operator"); + } + + result = ast_const(tname, tname->intern_val, expr); + result->overload_op_info = op_info; + result->kind = AST_LAMBDA; + result->flags = set_flag(result->flags, AST_OPERATOR_OVERLOAD); + } + + else if (token_match(TK_Identifier, TK_Colon)) { + Ast_Expr *typespec = parse_expr(); + set_flag_typespec(typespec); + + Ast_Expr *expr = parse_assign_expr(); + for (;;) { + if (token_match_pound(pctx->intern_foreign)) set_flag(flags, AST_FOREIGN); + else if (token_match_pound(pctx->intern_compiler_breakpoint)) set_flag(flags, AST_COMPILER_BREAKPOINT); + else break; + } + + result = ast_var(tname, typespec, tname->intern_val, expr); + } + + else if (token_match(TK_Identifier, TK_ColonAssign)) { + if (token_match_pound(pctx->intern_compiler_breakpoint)) set_flag(flags, AST_COMPILER_BREAKPOINT); + Ast_Expr *expr = parse_expr(); + result = ast_var(tname, 0, tname->intern_val, expr); + } + + else if (is_global && tname->kind != TK_End) { + compiler_error(tname, "Unexpected token: [%s] when parsing a declaration", name(tname->kind)); + } + + if (result) { + set_flag(result->flags, flags); + result->name = tname->intern_val; + result->unique_name = tname->intern_val; + } + + return result; +} + +CORE_Static void +parse_file(Ast_File *file) { + assert(file); + Scoped_Arena scratch(pctx->scratch); + + if (!file->filecontent.len) { + file->filecontent = os_read_file(pctx->perm, file->absolute_file_path); + if (file->filecontent.len == 0) { + compiler_error(file->pos, "Failed to open file \"%Q\"", file->absolute_file_path); + } + } + + pctx->currently_parsed_file = file; + pctx->currently_parsed_scope = file; + lex_restream(pctx, file->filecontent, file->absolute_file_path); + while (token_expect(SAME_SCOPE)) { + if (token_match_pound(pctx->intern_load)) { + parse_load(true); + continue; + } + else if (token_match_pound(pctx->intern_import)) { + parse_import(true); + continue; + } + else if (token_match_pound(pctx->intern_link)) { + Token *file = token_expect(TK_StringLit); + add(pctx->perm, &pctx->files_to_link, file); + continue; + } + + if (!file->pos) { + file->pos = token_get(); + } + + Ast_Decl *decl = parse_decl(true); + if (!decl) break; + + set_flag(decl->flags, AST_GLOBAL); + if (decl->kind == AST_STRUCT || decl->kind == AST_UNION) { + decl->type = pctx->type_type; + decl->type_val = type_incomplete(decl); + decl->state = DECL_RESOLVED; + } + + insert_into_scope(file, decl); + } + pctx->currently_parsed_scope = 0; + pctx->currently_parsed_file = 0; +} diff --git a/core_polymorph.cpp b/src/language/core_polymorph.cpp similarity index 97% rename from core_polymorph.cpp rename to src/language/core_polymorph.cpp index 5c60360..faa1e9a 100644 --- a/core_polymorph.cpp +++ b/src/language/core_polymorph.cpp @@ -1,552 +1,552 @@ - -CORE_Static Intern_String -get_unique_name() { - static uint64_t counter; - static char char_counter; - if (char_counter == 0 || char_counter > 'Z') char_counter = 'A'; - return pctx->internf("%c%uUnique", char_counter++, counter++); -} - -CORE_Static Intern_String -get_unique_name_for_decl(Ast_Decl *decl) { - static char char_counter; - if (char_counter == 0 || char_counter > 'Z') char_counter = 'A'; - return pctx->internf("%c%u_%Q", char_counter++, decl->di, decl->name); -} - -CORE_Static String core_type_to_string(Ast_Type *type); -Ast_Expr *create_typespec_from_type(Token *pos, Ast_Scope *parent_scope, Ast_Type *type) { - Ast_Expr *result = 0; - switch (type->kind) { - case TYPE_NONE: - case TYPE_COMPLETING: - case TYPE_INCOMPLETE: - case TYPE_POLYMORPH: - case TYPE_UNTYPED_BOOL: - case TYPE_UNTYPED_INT: - case TYPE_UNTYPED_FLOAT: - case TYPE_UNTYPED_STRING: { - invalid_codepath; - } break; - - case TYPE_TYPE: - case TYPE_S64: - case TYPE_S32: - case TYPE_S16: - case TYPE_S8: - case TYPE_INT: - case TYPE_CHAR: - case TYPE_U64: - case TYPE_U32: - case TYPE_U16: - case TYPE_U8: - case TYPE_F32: - case TYPE_F64: - case TYPE_BOOL: - case TYPE_STRING: - case TYPE_VOID: { - String s = core_type_to_string(type); - result = ast_ident(pos, pctx->intern(s)); - } break; - - case TYPE_POINTER: { - result = ast_expr_unary(pos, TK_Pointer, create_typespec_from_type(pos, parent_scope, type->base)); - } break; - case TYPE_ARRAY: { - Ast_Array *arr = ast_array(pos, create_typespec_from_type(pos, parent_scope, type->arr.base)); - arr->expr = ast_int(pos, type->arr.size); - arr->expr->parent_scope = parent_scope; - result = arr; - } break; - case TYPE_SLICE: { - Ast_Array *arr = ast_array(pos, create_typespec_from_type(pos, parent_scope, type->arr.base)); - result = arr; - } break; - case TYPE_STRUCT: - case TYPE_UNION: - case TYPE_ENUM: { - // @consider: Maybe instead of using AST_IDENT another case should be - // added like AST_RESOLVED or something because currently the identifier - // is a bit bogus. - - // We fill out the resolved_decl here because the typespecs for - // polymorphic types can be really complex and very context dependent. - // For example for a namespaced polymorph we would need to figure out the - // namespace, add binary expression etc. - Ast_Decl *decl = (Ast_Decl *)type->ast; - Ast_Atom *atom = ast_ident(pos, decl->name); - atom->resolved_decl = decl; - result = atom; - } break; - case TYPE_LAMBDA: { - // @warning: Not sure if this is correct, need to test! - Ast_Decl *decl = (Ast_Decl *)type->ast; - Ast_Atom *atom = ast_ident(pos, decl->name); - atom->resolved_decl = decl; - result = atom; - } break; - - invalid_default_case; - } - result->parent_scope = parent_scope; - return result; -} - -const int POLY_TYPE_REPLACEMENT = 0; -const int POLY_AST_REPLACEMENT = 1; - -struct Poly_Replacement { - int kind; - Intern_String poly_roli; - - Ast_Type *type; - Ast *ast; -}; - -CORE_Static void -extract_polymorph_type(Array *polys, Ast_Expr *polymorph_typespec, Ast_Type *type) { - assert(polymorph_typespec->flags & AST_TYPESPEC); - switch (polymorph_typespec->kind) { - case AST_UNARY: { - Ast_Unary *n = (Ast_Unary *)polymorph_typespec; - assert(n->op == TK_Pointer); - if (type->kind != TYPE_POINTER) { - compiler_error(polymorph_typespec->pos, "Passed in value's type doesn't match the polymorphic type"); - return; - } - extract_polymorph_type(polys, n->expr, type->base); - } break; - - case AST_ARRAY: { - Ast_Array *n = (Ast_Array *)polymorph_typespec; - if (type->kind != TYPE_ARRAY && type->kind != TYPE_SLICE) { - compiler_error(polymorph_typespec->pos, "Passed in value's type doesn't match the polymorphic type"); - return; - } - extract_polymorph_type(polys, n->base, type->base); - } break; - - case AST_CALL: { - Ast_Call *n = (Ast_Call *)polymorph_typespec; - bool is_aggregate = type->kind == TYPE_STRUCT || type->kind == TYPE_UNION; - bool is_poly = type->ast->flags & AST_POLYMORPH_INSTANCE; - if (!is_aggregate || !is_poly) { - compiler_error(polymorph_typespec->pos, "Passed in value's type doesn't match the polymorphic type"); - return; - } - Ast_Decl *decl = (Ast_Decl *)type->ast; - if (decl->polymorph_resolved_parameter_types.len != n->exprs.len) { - compiler_error(polymorph_typespec->pos, "Different types, the polymorph has different number of type arguments"); - return; - } - - for (int i = 0; i < n->exprs.len; i += 1) { - Ast_Type *t = decl->polymorph_resolved_parameter_types[i]; - Ast_Call_Item *spec = n->exprs[i]; - extract_polymorph_type(polys, spec->item, t); - } - } break; - - case AST_IDENT: { - Ast_Atom *n = (Ast_Atom *)polymorph_typespec; - bool is_poly = n->flags & AST_POLYMORPH; - if (is_poly) polys->add({POLY_TYPE_REPLACEMENT, n->intern_val, type}); - } break; - invalid_default_case; - } -} - -#define ast_create_copy(parent_scope, T, ast) (T *)ast__create_copy(parent_scope, sizeof(T), ast) -Ast *ast__create_copy(Ast_Scope *parent_scope, size_t size, Ast *ast) { - if (ast == 0) return 0; - Ast *result = (Ast *)push_copy(pctx->perm, ast, size); - result->parent_scope = parent_scope; - result->di = ++pctx->unique_ids; - return result; -} - -// We are not copying module and file Ast's -// We are not copying resolved data -Ast *ast_copy(Ast *ast, Ast_Scope *parent_scope, Array *repl) { - if (!ast) return 0; - Ast *result = 0; - switch (ast->kind) { - case AST_SCOPE: { - Ast_Scope *src = (Ast_Scope *)ast; - Ast_Scope *dst = ast_create_copy(parent_scope, Ast_Scope, src); - - dst->decls = {}; - For(src->decls) { - Ast_Decl *copy = (Ast_Decl *)ast_copy(it, src, repl); - add(pctx->perm, &dst->decls, copy); - } - - dst->stmts.init(pctx->perm, src->stmts.len); - For(src->stmts) { - Ast *copy = ast_copy(it, dst, repl); - dst->stmts.add(copy); - } - result = dst; - } break; - - case AST_MODULE: invalid_codepath; break; - case AST_FILE: invalid_codepath; break; - - case AST_IDENT: { - Ast_Atom *src = (Ast_Atom *)ast; - Ast_Atom *dst = ast_create_copy(parent_scope, Ast_Atom, ast); - - if (repl) { - // @todo: IF ITS STRUCT we only want to replace TYPESPECS - For(*repl) { - if (it.poly_roli != dst->intern_val) continue; - if (it.kind == POLY_AST_REPLACEMENT) { - dst = (Ast_Atom *)ast_copy(it.ast, parent_scope, 0); - } - else if (it.kind == POLY_TYPE_REPLACEMENT) { - type_complete(it.type); - dst = (Ast_Atom *)create_typespec_from_type(dst->pos, parent_scope, it.type); - } - else invalid_codepath; - } - } - - result = dst; - } break; - case AST_VALUE: { - Ast_Atom *src = (Ast_Atom *)ast; - Ast_Atom *dst = ast_create_copy(parent_scope, Ast_Atom, ast); - result = dst; - } break; - - case AST_INDEX: { - Ast_Index *src = (Ast_Index *)ast; - Ast_Index *dst = ast_create_copy(parent_scope, Ast_Index, ast); - dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); - dst->index = (Ast_Expr *)ast_copy(src->index, parent_scope, repl); - result = dst; - } break; - - case AST_UNARY: { - Ast_Unary *src = (Ast_Unary *)ast; - Ast_Unary *dst = ast_create_copy(parent_scope, Ast_Unary, ast); - dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); - result = dst; - } break; - - case AST_BINARY: { - Ast_Binary *src = (Ast_Binary *)ast; - Ast_Binary *dst = ast_create_copy(parent_scope, Ast_Binary, ast); - dst->left = (Ast_Expr *)ast_copy(src->left, parent_scope, repl); - dst->right = (Ast_Expr *)ast_copy(src->right, parent_scope, repl); - result = dst; - } break; - - case AST_CALL_ITEM: { - Ast_Call_Item *src = (Ast_Call_Item *)ast; - Ast_Call_Item *dst = ast_create_copy(parent_scope, Ast_Call_Item, ast); - dst->item = (Ast_Expr *)ast_copy(src->item, parent_scope, repl); - if (src->call_flags & CALL_INDEX) { - dst->index = (Ast_Expr *)ast_copy(src->index, parent_scope, repl); - } - else if (src->call_flags & CALL_NAME) { - dst->name = (Ast_Atom *)ast_copy(src->name, parent_scope, repl); - } - result = dst; - } break; - - case AST_COMPOUND: - case AST_CALL: { - Ast_Call *src = (Ast_Call *)ast; - Ast_Call *dst = ast_create_copy(parent_scope, Ast_Call, ast); - dst->name = (Ast_Expr *)ast_copy(src->name, parent_scope, repl); - - dst->exprs.init(pctx->perm, src->exprs.len); - For(src->exprs) { - auto copy = (Ast_Call_Item *)ast_copy(it, parent_scope, repl); - dst->exprs.add(copy); - } - result = dst; - } break; - - case AST_TYPE_OF: - case AST_LENGTH_OF: - case AST_ALIGN_OF: - case AST_SIZE_OF: - case AST_RUNTIME_ASSERT: - case AST_CONSTANT_ASSERT: { - Ast_Builtin *src = (Ast_Builtin *)ast; - Ast_Builtin *dst = ast_create_copy(parent_scope, Ast_Builtin, ast); - dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); - result = dst; - } break; - - case AST_SWITCH: { - Ast_Switch *src = (Ast_Switch *)ast; - Ast_Switch *dst = ast_create_copy(parent_scope, Ast_Switch, ast); - dst->value = (Ast_Expr *)ast_copy(src->value, parent_scope, repl); - dst->default_scope = (Ast_Scope *)ast_copy(src->default_scope, parent_scope, repl); - - dst->cases.init(pctx->perm, src->cases.len); - For(src->cases) { - auto copy = (Ast_Switch_Case *)ast_copy(it, parent_scope, repl); - assert(copy->kind == AST_SWITCH_CASE); - dst->cases.add(copy); - } - result = dst; - } break; - - case AST_SWITCH_CASE: { - Ast_Switch_Case *src = (Ast_Switch_Case *)ast; - Ast_Switch_Case *dst = ast_create_copy(parent_scope, Ast_Switch_Case, ast); - - dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); - dst->labels.init(pctx->perm, src->labels.len); - For(src->labels) { - auto copy = (Ast_Expr *)ast_copy(it, parent_scope, repl); - dst->labels.add(copy); - } - result = dst; - } break; - - case AST_VAR_UNPACK: { - Ast_Var_Unpack *src = (Ast_Var_Unpack *)ast; - Ast_Var_Unpack *dst = ast_create_copy(parent_scope, Ast_Var_Unpack, ast); - - dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); - dst->vars.init(pctx->perm, src->vars.len); - For(src->vars) { - auto copy = (Ast_Decl *)ast_copy(it, parent_scope, repl); - dst->vars.add(copy); - } - result = dst; - } break; - - case AST_DEFER: { - Ast_Defer *src = (Ast_Defer *)ast; - Ast_Defer *dst = ast_create_copy(parent_scope, Ast_Defer, ast); - dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); - } break; - - case AST_BREAK: - case AST_PASS: - case AST_GOTO: - case AST_CONTINUE: - case AST_COMPILER_BREAKPOINT_STMT: { - Ast *src = (Ast *)ast; - Ast *dst = ast_create_copy(parent_scope, Ast, ast); - result = dst; - } break; - - case AST_LABEL: - case AST_NAMESPACE: - case AST_STRUCT: - case AST_UNION: - case AST_ENUM: - case AST_LAMBDA: - case AST_TYPE: - case AST_CONST: - case AST_VAR: { - Ast_Decl *src = (Ast_Decl *)ast; - Ast_Decl *dst = ast_create_copy(parent_scope, Ast_Decl, ast); - // dst->overload_op_info = ast_create_copy(parent_scope, Ast_Operator_Info, src->overload_op_info); - // omitting polymorphs - // omitting polymorph parameters - dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); - dst->typespec = (Ast_Expr *)ast_copy(src->typespec, parent_scope, repl); - dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); - result = dst; - } break; - - case AST_ARRAY: { - Ast_Array *src = (Ast_Array *)ast; - Ast_Array *dst = ast_create_copy(parent_scope, Ast_Array, ast); - dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); - dst->base = (Ast_Expr *)ast_copy(src->base, parent_scope, repl); - result = dst; - } break; - - case AST_FOR: { - Ast_For *src = (Ast_For *)ast; - Ast_For *dst = ast_create_copy(parent_scope, Ast_For, ast); - dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); - dst->init = (Ast_Expr *)ast_copy(src->init, parent_scope, repl); - dst->cond = (Ast_Expr *)ast_copy(src->cond, parent_scope, repl); - dst->iter = (Ast_Expr *)ast_copy(src->iter, parent_scope, repl); - result = dst; - } break; - - case AST_IF: { - Ast_If *src = (Ast_If *)ast; - Ast_If *dst = ast_create_copy(parent_scope, Ast_If, ast); - dst->ifs.init(pctx->perm, src->ifs.len); - For(src->ifs) { - auto copy = (Ast_If_Node *)ast_copy(it, parent_scope, repl); - assert(copy->kind == AST_IF_NODE); - dst->ifs.add(copy); - } - result = dst; - } break; - - case AST_IF_NODE: { - Ast_If_Node *src = (Ast_If_Node *)ast; - Ast_If_Node *dst = ast_create_copy(parent_scope, Ast_If_Node, ast); - dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); - dst->init = (Ast_Binary *)ast_copy(src->init, parent_scope, repl); - dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); - result = dst; - } break; - - case AST_RETURN: { - Ast_Return *src = (Ast_Return *)ast; - Ast_Return *dst = ast_create_copy(parent_scope, Ast_Return, ast); - - dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); - result = dst; - } break; - - case AST_LAMBDA_EXPR: { - Ast_Lambda *src = (Ast_Lambda *)ast; - Ast_Lambda *dst = ast_create_copy(parent_scope, Ast_Lambda, ast); - - dst->args.init(pctx->perm, src->args.len); - For(src->args) { - if (it->flags & AST_IDENT_POLYMORPH) continue; - auto copy = (Ast_Decl *)ast_copy(it, parent_scope, repl); - dst->args.add(copy); - } - - dst->ret = (Ast_Expr *)ast_copy(src->ret, parent_scope, repl); - dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); - result = dst; - } break; - - default: assert(!"Invalid default case"); - } - assert(result); - - unset_flag(result->flags, AST_POLYMORPH | AST_PARENT_POLYMORPH); - set_flag(result->flags, AST_POLYMORPH_INSTANCE); - - return result; -} - -// @todo: check for multiple poly_roli of same name - -Ast_Decl *get_or_instantiate_polymorph_type(Token *pos, Ast_Decl *poly, Array params, Ast_Scope *field_access_scope) { - if (params.len != poly->polymorph_parameters.len) compiler_error(pos, "Invalid count of polymorphic arguments"); - Scoped_Arena scratch(pctx->scratch); - - Array resolved_type_params = {scratch.arena}; - Array repl = {scratch.arena}; - - int i = 0; - uint64_t hash = 91; - For(params) { - bool indexed = (it->flags & CALL_INDEX); - bool named = (it->flags & CALL_NAME); - if (indexed == false && named == false) compiler_error(it->pos, "Polymorphic type cannot have named/indexed arguments"); - - Ast_Decl *poly_decl = poly->polymorph_parameters[i++]; - resolve_decl(poly_decl); - if (poly_decl->type != pctx->type_type) compiler_error(poly_decl->pos, "Invalid type of polymorphic struct argument"); - - Operand op = resolve_expr(it->item, AST_CANT_BE_NULL, 0, field_access_scope); - if (!op.is_const) compiler_error(it->pos, "Argument is required to be compile time known"); - if (op.type != pctx->type_type) compiler_error(it->pos, "Struct argument required to be a type"); - - hash = hash_mix(hash, hash_ptr(op.type_val)); - resolved_type_params.add(op.type_val); - repl.add({POLY_AST_REPLACEMENT, poly_decl->name, 0, it->item}); - } - - if (repl.len == 0) compiler_error(pos, "No polymorphic variables found to replace"); - - Ast_Decl *result = 0; - For(poly->polymorphs) { - if (it->polymorph_hash == hash) { - result = it; - break; - } - } - - if (!result) { - result = (Ast_Decl *)ast_copy(poly, poly->parent_scope, &repl); - result->type_val = type_incomplete(result); - result->polymorph_hash = hash; - result->polymorph_resolved_parameter_types = resolved_type_params.tight_copy(pctx->perm); - result->unique_name = get_unique_name_for_decl(result); - assert(result->di != poly->di); - - if (!poly->polymorphs.allocator) poly->polymorphs.allocator = pctx->heap; - poly->polymorphs.add(result); - } - - resolve_decl(result); - type_complete(result->type_val); - return result; -} - -Ast_Decl *get_or_instantiate_polymorph_lambda(Token *pos, Ast_Decl *poly, Array params, Ast_Scope *field_access_scope) { - if (params.len != poly->polymorph_parameters.len) compiler_error(pos, "Invalid count of polymorphic arguments"); - Scoped_Arena scratch(pctx->scratch); - - Array resolved_type_params = {scratch.arena}; - Array repl = {scratch.arena}; - - int i = 0; - uint64_t hash = 91; - For(params) { - Ast_Decl *poly_decl = poly->polymorph_parameters[i++]; - if (poly_decl->flags & AST_IDENT_POLYMORPH) { - resolve_decl(poly_decl); - if (poly_decl->type != pctx->type_type) compiler_error(poly_decl->pos, "Invalid type of polymorphic struct argument"); - - Operand op = resolve_expr(it->item, AST_CANT_BE_NULL, 0, field_access_scope); - if (!op.is_const) compiler_error(it->pos, "Argument is required to be compile time known"); - if (op.type != pctx->type_type) compiler_error(it->pos, "Struct argument required to be a type"); - - hash = hash_mix(hash, hash_ptr(op.type_val)); - resolved_type_params.add(op.type_val); - repl.add({POLY_AST_REPLACEMENT, poly_decl->name, 0, it->item}); - } - else { - Operand op = resolve_expr(it->item, AST_CANT_BE_NULL, 0, field_access_scope); - try_converting_untyped_to_default_type(&op); - try_propagating_resolved_type_to_untyped_literals(it->item, op.type); - // type_complete(op.type); - assert(it->item->resolved_type == op.type); - - extract_polymorph_type(&repl, poly_decl->typespec, op.type); - - hash = hash_mix(hash, hash_ptr(op.type)); - resolved_type_params.add(op.type); - } - } - - if (repl.len == 0) compiler_error(pos, "No polymorphic variables found to replace"); - - Ast_Decl *result = 0; - For(poly->polymorphs) { - if (it->polymorph_hash == hash) { - result = it; - break; - } - } - - if (!result) { - result = (Ast_Decl *)ast_copy(poly, poly->parent_scope, &repl); - result->polymorph_hash = hash; - result->polymorph_resolved_parameter_types = resolved_type_params.tight_copy(pctx->perm); - result->unique_name = get_unique_name_for_decl(result); - - if (!poly->polymorphs.allocator) poly->polymorphs.allocator = pctx->heap; - poly->polymorphs.add(result); - - assert(result->polymorph_resolved_parameter_types.len == poly->polymorph_parameters.len); - assert(result->di != poly->di); - } - - resolve_decl(result); - return result; -} + +CORE_Static Intern_String +get_unique_name() { + static uint64_t counter; + static char char_counter; + if (char_counter == 0 || char_counter > 'Z') char_counter = 'A'; + return pctx->internf("%c%uUnique", char_counter++, counter++); +} + +CORE_Static Intern_String +get_unique_name_for_decl(Ast_Decl *decl) { + static char char_counter; + if (char_counter == 0 || char_counter > 'Z') char_counter = 'A'; + return pctx->internf("%c%u_%Q", char_counter++, decl->di, decl->name); +} + +CORE_Static String core_type_to_string(Ast_Type *type); +Ast_Expr *create_typespec_from_type(Token *pos, Ast_Scope *parent_scope, Ast_Type *type) { + Ast_Expr *result = 0; + switch (type->kind) { + case TYPE_NONE: + case TYPE_COMPLETING: + case TYPE_INCOMPLETE: + case TYPE_POLYMORPH: + case TYPE_UNTYPED_BOOL: + case TYPE_UNTYPED_INT: + case TYPE_UNTYPED_FLOAT: + case TYPE_UNTYPED_STRING: { + invalid_codepath; + } break; + + case TYPE_TYPE: + case TYPE_S64: + case TYPE_S32: + case TYPE_S16: + case TYPE_S8: + case TYPE_INT: + case TYPE_CHAR: + case TYPE_U64: + case TYPE_U32: + case TYPE_U16: + case TYPE_U8: + case TYPE_F32: + case TYPE_F64: + case TYPE_BOOL: + case TYPE_STRING: + case TYPE_VOID: { + String s = core_type_to_string(type); + result = ast_ident(pos, pctx->intern(s)); + } break; + + case TYPE_POINTER: { + result = ast_expr_unary(pos, TK_Pointer, create_typespec_from_type(pos, parent_scope, type->base)); + } break; + case TYPE_ARRAY: { + Ast_Array *arr = ast_array(pos, create_typespec_from_type(pos, parent_scope, type->arr.base)); + arr->expr = ast_int(pos, type->arr.size); + arr->expr->parent_scope = parent_scope; + result = arr; + } break; + case TYPE_SLICE: { + Ast_Array *arr = ast_array(pos, create_typespec_from_type(pos, parent_scope, type->arr.base)); + result = arr; + } break; + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_ENUM: { + // @consider: Maybe instead of using AST_IDENT another case should be + // added like AST_RESOLVED or something because currently the identifier + // is a bit bogus. + + // We fill out the resolved_decl here because the typespecs for + // polymorphic types can be really complex and very context dependent. + // For example for a namespaced polymorph we would need to figure out the + // namespace, add binary expression etc. + Ast_Decl *decl = (Ast_Decl *)type->ast; + Ast_Atom *atom = ast_ident(pos, decl->name); + atom->resolved_decl = decl; + result = atom; + } break; + case TYPE_LAMBDA: { + // @warning: Not sure if this is correct, need to test! + Ast_Decl *decl = (Ast_Decl *)type->ast; + Ast_Atom *atom = ast_ident(pos, decl->name); + atom->resolved_decl = decl; + result = atom; + } break; + + invalid_default_case; + } + result->parent_scope = parent_scope; + return result; +} + +const int POLY_TYPE_REPLACEMENT = 0; +const int POLY_AST_REPLACEMENT = 1; + +struct Poly_Replacement { + int kind; + Intern_String poly_roli; + + Ast_Type *type; + Ast *ast; +}; + +CORE_Static void +extract_polymorph_type(Array *polys, Ast_Expr *polymorph_typespec, Ast_Type *type) { + assert(polymorph_typespec->flags & AST_TYPESPEC); + switch (polymorph_typespec->kind) { + case AST_UNARY: { + Ast_Unary *n = (Ast_Unary *)polymorph_typespec; + assert(n->op == TK_Pointer); + if (type->kind != TYPE_POINTER) { + compiler_error(polymorph_typespec->pos, "Passed in value's type doesn't match the polymorphic type"); + return; + } + extract_polymorph_type(polys, n->expr, type->base); + } break; + + case AST_ARRAY: { + Ast_Array *n = (Ast_Array *)polymorph_typespec; + if (type->kind != TYPE_ARRAY && type->kind != TYPE_SLICE) { + compiler_error(polymorph_typespec->pos, "Passed in value's type doesn't match the polymorphic type"); + return; + } + extract_polymorph_type(polys, n->base, type->base); + } break; + + case AST_CALL: { + Ast_Call *n = (Ast_Call *)polymorph_typespec; + bool is_aggregate = type->kind == TYPE_STRUCT || type->kind == TYPE_UNION; + bool is_poly = type->ast->flags & AST_POLYMORPH_INSTANCE; + if (!is_aggregate || !is_poly) { + compiler_error(polymorph_typespec->pos, "Passed in value's type doesn't match the polymorphic type"); + return; + } + Ast_Decl *decl = (Ast_Decl *)type->ast; + if (decl->polymorph_resolved_parameter_types.len != n->exprs.len) { + compiler_error(polymorph_typespec->pos, "Different types, the polymorph has different number of type arguments"); + return; + } + + for (int i = 0; i < n->exprs.len; i += 1) { + Ast_Type *t = decl->polymorph_resolved_parameter_types[i]; + Ast_Call_Item *spec = n->exprs[i]; + extract_polymorph_type(polys, spec->item, t); + } + } break; + + case AST_IDENT: { + Ast_Atom *n = (Ast_Atom *)polymorph_typespec; + bool is_poly = n->flags & AST_POLYMORPH; + if (is_poly) polys->add({POLY_TYPE_REPLACEMENT, n->intern_val, type}); + } break; + invalid_default_case; + } +} + +#define ast_create_copy(parent_scope, T, ast) (T *)ast__create_copy(parent_scope, sizeof(T), ast) +Ast *ast__create_copy(Ast_Scope *parent_scope, size_t size, Ast *ast) { + if (ast == 0) return 0; + Ast *result = (Ast *)push_copy(pctx->perm, ast, size); + result->parent_scope = parent_scope; + result->di = ++pctx->unique_ids; + return result; +} + +// We are not copying module and file Ast's +// We are not copying resolved data +Ast *ast_copy(Ast *ast, Ast_Scope *parent_scope, Array *repl) { + if (!ast) return 0; + Ast *result = 0; + switch (ast->kind) { + case AST_SCOPE: { + Ast_Scope *src = (Ast_Scope *)ast; + Ast_Scope *dst = ast_create_copy(parent_scope, Ast_Scope, src); + + dst->decls = {}; + For(src->decls) { + Ast_Decl *copy = (Ast_Decl *)ast_copy(it, src, repl); + add(pctx->perm, &dst->decls, copy); + } + + dst->stmts.init(pctx->perm, src->stmts.len); + For(src->stmts) { + Ast *copy = ast_copy(it, dst, repl); + dst->stmts.add(copy); + } + result = dst; + } break; + + case AST_MODULE: invalid_codepath; break; + case AST_FILE: invalid_codepath; break; + + case AST_IDENT: { + Ast_Atom *src = (Ast_Atom *)ast; + Ast_Atom *dst = ast_create_copy(parent_scope, Ast_Atom, ast); + + if (repl) { + // @todo: IF ITS STRUCT we only want to replace TYPESPECS + For(*repl) { + if (it.poly_roli != dst->intern_val) continue; + if (it.kind == POLY_AST_REPLACEMENT) { + dst = (Ast_Atom *)ast_copy(it.ast, parent_scope, 0); + } + else if (it.kind == POLY_TYPE_REPLACEMENT) { + type_complete(it.type); + dst = (Ast_Atom *)create_typespec_from_type(dst->pos, parent_scope, it.type); + } + else invalid_codepath; + } + } + + result = dst; + } break; + case AST_VALUE: { + Ast_Atom *src = (Ast_Atom *)ast; + Ast_Atom *dst = ast_create_copy(parent_scope, Ast_Atom, ast); + result = dst; + } break; + + case AST_INDEX: { + Ast_Index *src = (Ast_Index *)ast; + Ast_Index *dst = ast_create_copy(parent_scope, Ast_Index, ast); + dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); + dst->index = (Ast_Expr *)ast_copy(src->index, parent_scope, repl); + result = dst; + } break; + + case AST_UNARY: { + Ast_Unary *src = (Ast_Unary *)ast; + Ast_Unary *dst = ast_create_copy(parent_scope, Ast_Unary, ast); + dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); + result = dst; + } break; + + case AST_BINARY: { + Ast_Binary *src = (Ast_Binary *)ast; + Ast_Binary *dst = ast_create_copy(parent_scope, Ast_Binary, ast); + dst->left = (Ast_Expr *)ast_copy(src->left, parent_scope, repl); + dst->right = (Ast_Expr *)ast_copy(src->right, parent_scope, repl); + result = dst; + } break; + + case AST_CALL_ITEM: { + Ast_Call_Item *src = (Ast_Call_Item *)ast; + Ast_Call_Item *dst = ast_create_copy(parent_scope, Ast_Call_Item, ast); + dst->item = (Ast_Expr *)ast_copy(src->item, parent_scope, repl); + if (src->call_flags & CALL_INDEX) { + dst->index = (Ast_Expr *)ast_copy(src->index, parent_scope, repl); + } + else if (src->call_flags & CALL_NAME) { + dst->name = (Ast_Atom *)ast_copy(src->name, parent_scope, repl); + } + result = dst; + } break; + + case AST_COMPOUND: + case AST_CALL: { + Ast_Call *src = (Ast_Call *)ast; + Ast_Call *dst = ast_create_copy(parent_scope, Ast_Call, ast); + dst->name = (Ast_Expr *)ast_copy(src->name, parent_scope, repl); + + dst->exprs.init(pctx->perm, src->exprs.len); + For(src->exprs) { + auto copy = (Ast_Call_Item *)ast_copy(it, parent_scope, repl); + dst->exprs.add(copy); + } + result = dst; + } break; + + case AST_TYPE_OF: + case AST_LENGTH_OF: + case AST_ALIGN_OF: + case AST_SIZE_OF: + case AST_RUNTIME_ASSERT: + case AST_CONSTANT_ASSERT: { + Ast_Builtin *src = (Ast_Builtin *)ast; + Ast_Builtin *dst = ast_create_copy(parent_scope, Ast_Builtin, ast); + dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); + result = dst; + } break; + + case AST_SWITCH: { + Ast_Switch *src = (Ast_Switch *)ast; + Ast_Switch *dst = ast_create_copy(parent_scope, Ast_Switch, ast); + dst->value = (Ast_Expr *)ast_copy(src->value, parent_scope, repl); + dst->default_scope = (Ast_Scope *)ast_copy(src->default_scope, parent_scope, repl); + + dst->cases.init(pctx->perm, src->cases.len); + For(src->cases) { + auto copy = (Ast_Switch_Case *)ast_copy(it, parent_scope, repl); + assert(copy->kind == AST_SWITCH_CASE); + dst->cases.add(copy); + } + result = dst; + } break; + + case AST_SWITCH_CASE: { + Ast_Switch_Case *src = (Ast_Switch_Case *)ast; + Ast_Switch_Case *dst = ast_create_copy(parent_scope, Ast_Switch_Case, ast); + + dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); + dst->labels.init(pctx->perm, src->labels.len); + For(src->labels) { + auto copy = (Ast_Expr *)ast_copy(it, parent_scope, repl); + dst->labels.add(copy); + } + result = dst; + } break; + + case AST_VAR_UNPACK: { + Ast_Var_Unpack *src = (Ast_Var_Unpack *)ast; + Ast_Var_Unpack *dst = ast_create_copy(parent_scope, Ast_Var_Unpack, ast); + + dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); + dst->vars.init(pctx->perm, src->vars.len); + For(src->vars) { + auto copy = (Ast_Decl *)ast_copy(it, parent_scope, repl); + dst->vars.add(copy); + } + result = dst; + } break; + + case AST_DEFER: { + Ast_Defer *src = (Ast_Defer *)ast; + Ast_Defer *dst = ast_create_copy(parent_scope, Ast_Defer, ast); + dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); + } break; + + case AST_BREAK: + case AST_PASS: + case AST_GOTO: + case AST_CONTINUE: + case AST_COMPILER_BREAKPOINT_STMT: { + Ast *src = (Ast *)ast; + Ast *dst = ast_create_copy(parent_scope, Ast, ast); + result = dst; + } break; + + case AST_LABEL: + case AST_NAMESPACE: + case AST_STRUCT: + case AST_UNION: + case AST_ENUM: + case AST_LAMBDA: + case AST_TYPE: + case AST_CONST: + case AST_VAR: { + Ast_Decl *src = (Ast_Decl *)ast; + Ast_Decl *dst = ast_create_copy(parent_scope, Ast_Decl, ast); + // dst->overload_op_info = ast_create_copy(parent_scope, Ast_Operator_Info, src->overload_op_info); + // omitting polymorphs + // omitting polymorph parameters + dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); + dst->typespec = (Ast_Expr *)ast_copy(src->typespec, parent_scope, repl); + dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); + result = dst; + } break; + + case AST_ARRAY: { + Ast_Array *src = (Ast_Array *)ast; + Ast_Array *dst = ast_create_copy(parent_scope, Ast_Array, ast); + dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); + dst->base = (Ast_Expr *)ast_copy(src->base, parent_scope, repl); + result = dst; + } break; + + case AST_FOR: { + Ast_For *src = (Ast_For *)ast; + Ast_For *dst = ast_create_copy(parent_scope, Ast_For, ast); + dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); + dst->init = (Ast_Expr *)ast_copy(src->init, parent_scope, repl); + dst->cond = (Ast_Expr *)ast_copy(src->cond, parent_scope, repl); + dst->iter = (Ast_Expr *)ast_copy(src->iter, parent_scope, repl); + result = dst; + } break; + + case AST_IF: { + Ast_If *src = (Ast_If *)ast; + Ast_If *dst = ast_create_copy(parent_scope, Ast_If, ast); + dst->ifs.init(pctx->perm, src->ifs.len); + For(src->ifs) { + auto copy = (Ast_If_Node *)ast_copy(it, parent_scope, repl); + assert(copy->kind == AST_IF_NODE); + dst->ifs.add(copy); + } + result = dst; + } break; + + case AST_IF_NODE: { + Ast_If_Node *src = (Ast_If_Node *)ast; + Ast_If_Node *dst = ast_create_copy(parent_scope, Ast_If_Node, ast); + dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); + dst->init = (Ast_Binary *)ast_copy(src->init, parent_scope, repl); + dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); + result = dst; + } break; + + case AST_RETURN: { + Ast_Return *src = (Ast_Return *)ast; + Ast_Return *dst = ast_create_copy(parent_scope, Ast_Return, ast); + + dst->expr = (Ast_Expr *)ast_copy(src->expr, parent_scope, repl); + result = dst; + } break; + + case AST_LAMBDA_EXPR: { + Ast_Lambda *src = (Ast_Lambda *)ast; + Ast_Lambda *dst = ast_create_copy(parent_scope, Ast_Lambda, ast); + + dst->args.init(pctx->perm, src->args.len); + For(src->args) { + if (it->flags & AST_IDENT_POLYMORPH) continue; + auto copy = (Ast_Decl *)ast_copy(it, parent_scope, repl); + dst->args.add(copy); + } + + dst->ret = (Ast_Expr *)ast_copy(src->ret, parent_scope, repl); + dst->scope = (Ast_Scope *)ast_copy(src->scope, parent_scope, repl); + result = dst; + } break; + + default: assert(!"Invalid default case"); + } + assert(result); + + unset_flag(result->flags, AST_POLYMORPH | AST_PARENT_POLYMORPH); + set_flag(result->flags, AST_POLYMORPH_INSTANCE); + + return result; +} + +// @todo: check for multiple poly_roli of same name + +Ast_Decl *get_or_instantiate_polymorph_type(Token *pos, Ast_Decl *poly, Array params, Ast_Scope *field_access_scope) { + if (params.len != poly->polymorph_parameters.len) compiler_error(pos, "Invalid count of polymorphic arguments"); + Scoped_Arena scratch(pctx->scratch); + + Array resolved_type_params = {scratch.arena}; + Array repl = {scratch.arena}; + + int i = 0; + uint64_t hash = 91; + For(params) { + bool indexed = (it->flags & CALL_INDEX); + bool named = (it->flags & CALL_NAME); + if (indexed == false && named == false) compiler_error(it->pos, "Polymorphic type cannot have named/indexed arguments"); + + Ast_Decl *poly_decl = poly->polymorph_parameters[i++]; + resolve_decl(poly_decl); + if (poly_decl->type != pctx->type_type) compiler_error(poly_decl->pos, "Invalid type of polymorphic struct argument"); + + Operand op = resolve_expr(it->item, AST_CANT_BE_NULL, 0, field_access_scope); + if (!op.is_const) compiler_error(it->pos, "Argument is required to be compile time known"); + if (op.type != pctx->type_type) compiler_error(it->pos, "Struct argument required to be a type"); + + hash = hash_mix(hash, hash_ptr(op.type_val)); + resolved_type_params.add(op.type_val); + repl.add({POLY_AST_REPLACEMENT, poly_decl->name, 0, it->item}); + } + + if (repl.len == 0) compiler_error(pos, "No polymorphic variables found to replace"); + + Ast_Decl *result = 0; + For(poly->polymorphs) { + if (it->polymorph_hash == hash) { + result = it; + break; + } + } + + if (!result) { + result = (Ast_Decl *)ast_copy(poly, poly->parent_scope, &repl); + result->type_val = type_incomplete(result); + result->polymorph_hash = hash; + result->polymorph_resolved_parameter_types = resolved_type_params.tight_copy(pctx->perm); + result->unique_name = get_unique_name_for_decl(result); + assert(result->di != poly->di); + + if (!poly->polymorphs.allocator) poly->polymorphs.allocator = pctx->heap; + poly->polymorphs.add(result); + } + + resolve_decl(result); + type_complete(result->type_val); + return result; +} + +Ast_Decl *get_or_instantiate_polymorph_lambda(Token *pos, Ast_Decl *poly, Array params, Ast_Scope *field_access_scope) { + if (params.len != poly->polymorph_parameters.len) compiler_error(pos, "Invalid count of polymorphic arguments"); + Scoped_Arena scratch(pctx->scratch); + + Array resolved_type_params = {scratch.arena}; + Array repl = {scratch.arena}; + + int i = 0; + uint64_t hash = 91; + For(params) { + Ast_Decl *poly_decl = poly->polymorph_parameters[i++]; + if (poly_decl->flags & AST_IDENT_POLYMORPH) { + resolve_decl(poly_decl); + if (poly_decl->type != pctx->type_type) compiler_error(poly_decl->pos, "Invalid type of polymorphic struct argument"); + + Operand op = resolve_expr(it->item, AST_CANT_BE_NULL, 0, field_access_scope); + if (!op.is_const) compiler_error(it->pos, "Argument is required to be compile time known"); + if (op.type != pctx->type_type) compiler_error(it->pos, "Struct argument required to be a type"); + + hash = hash_mix(hash, hash_ptr(op.type_val)); + resolved_type_params.add(op.type_val); + repl.add({POLY_AST_REPLACEMENT, poly_decl->name, 0, it->item}); + } + else { + Operand op = resolve_expr(it->item, AST_CANT_BE_NULL, 0, field_access_scope); + try_converting_untyped_to_default_type(&op); + try_propagating_resolved_type_to_untyped_literals(it->item, op.type); + // type_complete(op.type); + assert(it->item->resolved_type == op.type); + + extract_polymorph_type(&repl, poly_decl->typespec, op.type); + + hash = hash_mix(hash, hash_ptr(op.type)); + resolved_type_params.add(op.type); + } + } + + if (repl.len == 0) compiler_error(pos, "No polymorphic variables found to replace"); + + Ast_Decl *result = 0; + For(poly->polymorphs) { + if (it->polymorph_hash == hash) { + result = it; + break; + } + } + + if (!result) { + result = (Ast_Decl *)ast_copy(poly, poly->parent_scope, &repl); + result->polymorph_hash = hash; + result->polymorph_resolved_parameter_types = resolved_type_params.tight_copy(pctx->perm); + result->unique_name = get_unique_name_for_decl(result); + + if (!poly->polymorphs.allocator) poly->polymorphs.allocator = pctx->heap; + poly->polymorphs.add(result); + + assert(result->polymorph_resolved_parameter_types.len == poly->polymorph_parameters.len); + assert(result->di != poly->di); + } + + resolve_decl(result); + return result; +} diff --git a/core_printer.cpp b/src/language/core_printer.cpp similarity index 96% rename from core_printer.cpp rename to src/language/core_printer.cpp index f1d04ff..e61acc2 100644 --- a/core_printer.cpp +++ b/src/language/core_printer.cpp @@ -1,375 +1,375 @@ - -// @todo @cleanup ?? -// :PrinterCleanup -// Instead of relying on global state, this probably should -// use a String_Builder or some kind of Builder context + macros - -CORE_Static String -core_type_to_string(Ast_Type *type) { - - // @todo: use get_name_of_type instead of duplicating the typename dispatch table - switch (type->kind) { - case TYPE_NONE: return ""_s; break; - case TYPE_S64: return "S64"_s; break; - case TYPE_S32: return "S32"_s; break; - case TYPE_S16: return "S16"_s; break; - case TYPE_S8: return "S8"_s; break; - - case TYPE_CHAR: return "char"_s; - case TYPE_UCHAR: return "uchar"_s; - case TYPE_INT: return "int"_s; - case TYPE_UINT: return "uint"_s; - case TYPE_LONG: return "long"_s; - case TYPE_ULONG: return "ulong"_s; - case TYPE_LLONG: return "llong"_s; - case TYPE_ULLONG: return "ullong"_s; - case TYPE_SHORT: return "short"_s; - case TYPE_USHORT: return "ushort"_s; - - case TYPE_U64: return "U64"_s; break; - case TYPE_U32: return "U32"_s; break; - case TYPE_U16: return "U16"_s; break; - case TYPE_U8: return "U8"_s; break; - case TYPE_F32: return "F32"_s; break; - case TYPE_F64: return "F64"_s; break; - case TYPE_BOOL: return "bool"_s; break; - case TYPE_STRING: return "String"_s; break; - case TYPE_VOID: return "void"_s; break; - case TYPE_POINTER: { - String base = core_type_to_string(type->base); - return pctx->fmt("*%Q", base); - } break; - - case TYPE_LAMBDA: { - String_Builder *b = &pctx->helper_builder; - b->addf("("); - Array &args = type->func.args; - For(args) { - String t = core_type_to_string(it); - b->addf("%Q", t); - if (!args.is_last(it)) b->addf(", "); - } - b->addf(")"); - if (type->func.ret) { - String t = core_type_to_string(type->func.ret); - b->addf("%Q", t); - } - String result = string_flatten(pctx->perm, b); - return result; - } break; - - case TYPE_STRUCT: - case TYPE_UNION: - case TYPE_ENUM: { - // @fixme: we probably want a string on Ast_Type - // so that we don't have to reach into Ast_Decl - // for Structs Enums etc. - Ast_Decl *decl = (Ast_Decl *)type->ast; - return decl->name.s; - } break; - - case TYPE_ARRAY: { - String base = core_type_to_string(type->base); - return pctx->fmt("[%u]%Q", type->arr.size, base); - } break; - - case TYPE_SLICE: { - String base = core_type_to_string(type->base); - return pctx->fmt("[]%Q", base); - } break; - - case TYPE_TYPE: return "Type"_s; break; - case TYPE_UNTYPED_BOOL: return "UntypedBool"_s; break; - case TYPE_UNTYPED_INT: return "UntypedInt"_s; break; - case TYPE_UNTYPED_FLOAT: return "UntypedFloat"_s; break; - case TYPE_UNTYPED_STRING: return "UntypedString"_s; break; - - case TYPE_COMPLETING: invalid_codepath; break; - case TYPE_INCOMPLETE: invalid_codepath; break; - case TYPE_POLYMORPH: - invalid_codepath; - break; - - invalid_default_case; - } - invalid_return; -} - -void core__stringify(Ast *ast) { - if (!ast) return; - gen("@%u", ast->di); - switch (ast->kind) { - case AST_SCOPE: { - Ast_Scope *n = (Ast_Scope *)ast; - - global_indent += 1; - genln("@%u", n->di); - For(n->decls) { - genln(""); - core__stringify(it); - } - For(n->stmts) { - genln(""); - core__stringify(it); - } - global_indent -= 1; - } break; - - case AST_MODULE: invalid_codepath; break; - case AST_FILE: invalid_codepath; break; - - case AST_IDENT: { - Ast_Atom *n = (Ast_Atom *)ast; - gen("%Q", n->intern_val); - } break; - - case AST_VALUE: { - Ast_Atom *n = (Ast_Atom *)ast; //@todo: proper value - gen("%Q", n->pos->string); - } break; - - case AST_INDEX: { - Ast_Index *n = (Ast_Index *)ast; - core__stringify(n->expr); - gen("["); - core__stringify(n->index); - gen("]"); - } break; - - case AST_UNARY: { - Ast_Unary *n = (Ast_Unary *)ast; - gen("%Q", n->pos->string); - core__stringify(n->expr); - } break; - - case AST_BINARY: { - Ast_Binary *n = (Ast_Binary *)ast; - core__stringify(n->left); - gen("%Q", n->pos->string); - core__stringify(n->right); - } break; - - case AST_CALL_ITEM: { - Ast_Call_Item *n = (Ast_Call_Item *)ast; - if (n->call_flags & CALL_INDEX) { - core__stringify(n->index); - gen(" = "); - } - else if (n->call_flags & CALL_NAME) { - core__stringify(n->name); - gen(" = "); - } - core__stringify(n->item); - - } break; - - case AST_CALL: { - Ast_Call *n = (Ast_Call *)ast; - core__stringify(n->name); - gen("("); - For(n->exprs) { - core__stringify(it); - if (!n->exprs.is_last(it)) gen(","); - } - gen(")"); - } break; - case AST_COMPOUND: { - Ast_Call *n = (Ast_Call *)ast; - core__stringify(n->name); - - gen("{"); - global_indent += 1; - For(n->exprs) { - genln(""); - core__stringify(it); - gen(","); - } - global_indent -= 1; - genln("}"); - } break; - - case AST_TYPE_OF: - gen("typeof"); - goto builtin; - case AST_LENGTH_OF: - gen("LengthOf"); - goto builtin; - case AST_ALIGN_OF: - gen("alignof"); - goto builtin; - case AST_SIZE_OF: - gen("sizeof"); - goto builtin; - case AST_RUNTIME_ASSERT: - gen("Assert"); - goto builtin; - case AST_CONSTANT_ASSERT: - gen("#Assert"); - goto builtin; - builtin : { - Ast_Builtin *n = (Ast_Builtin *)ast; - - gen("("); - core__stringify(n->expr); - gen(")"); - } break; - - case AST_SWITCH: { - Ast_Switch *n = (Ast_Switch *)ast; - - core__stringify(n->value); - For(n->cases) { - core__stringify(it); - } - core__stringify(n->default_scope); - } break; - - case AST_SWITCH_CASE: { - Ast_Switch_Case *n = (Ast_Switch_Case *)ast; - - For(n->labels) { - core__stringify(it); - } - core__stringify(n->scope); - } break; - - case AST_VAR_UNPACK: { - Ast_Var_Unpack *n = (Ast_Var_Unpack *)ast; - For(n->vars) { - core__stringify(it); - if (!n->vars.is_last(it)) gen(","); - } - gen(" = "); - core__stringify(n->expr); - } break; - - case AST_DEFER: { - auto n = (Ast_Defer *)ast; - genln("defer"); - core__stringify(n->scope); - } break; - - case AST_PASS: { - genln("pass"); - } break; - - case AST_CONTINUE: { - genln("continue"); - } break; - - case AST_BREAK: { - genln("break"); - } break; - - case AST_NAMESPACE: { - Ast_Decl *n = (Ast_Decl *)ast; - gen("%Q :: NAMESPACE", n->name); - } break; - - case AST_UNION: - case AST_STRUCT: { - Ast_Decl *n = (Ast_Decl *)ast; - genln("%Q :: %s", n->name, n->kind == AST_STRUCT ? "struct" : "union"); - core__stringify(n->scope); - } break; - - case AST_ENUM: { - Ast_Decl *n = (Ast_Decl *)ast; - genln("%Q :: enum", n->name); - core__stringify(n->scope); - } break; - - case AST_LAMBDA: { - Ast_Decl *n = (Ast_Decl *)ast; - genln("%Q :: ", n->name); - core__stringify(n->lambda); // @cleanup - } break; - - // @cleanup: what is this used for? - case AST_TYPE: { - invalid_codepath; - } break; - - case AST_CONST: { - Ast_Decl *n = (Ast_Decl *)ast; - gen("%Q :: CONST value @todo", n->name); - } break; - - case AST_VAR: { - Ast_Decl *n = (Ast_Decl *)ast; - gen("%Q: %Q", n->name, core_type_to_string(n->type)); - if (n->expr) { - gen(" = "); - core__stringify(n->expr); - } - } break; - - case AST_ARRAY: { - Ast_Array *n = (Ast_Array *)ast; - - gen("["); - core__stringify(n->expr); - gen("]"); - core__stringify(n->base); - } break; - - case AST_FOR: { - Ast_For *n = (Ast_For *)ast; - gen("for "); - core__stringify(n->init); - if (n->cond) gen(", "); - core__stringify(n->cond); - if (n->iter) gen(", "); - core__stringify(n->iter); - core__stringify(n->scope); - } break; - - case AST_IF: { - Ast_If *n = (Ast_If *)ast; - - int i = 0; - For(n->ifs) { - if (i == 0) gen("if "); - else if (it->expr) gen("elif "); - else gen("else "); - - core__stringify(it->expr); - if (it->init) gen(", "); - core__stringify(it->init); - core__stringify(it->scope); - - i += 1; - } - } break; - - case AST_RETURN: { - Ast_Return *n = (Ast_Return *)ast; - gen("return "); - core__stringify(n->expr); - } break; - - case AST_LAMBDA_EXPR: { - Ast_Lambda *n = (Ast_Lambda *)ast; - - gen("("); - For(n->args) { - core__stringify(it); - if (!n->args.is_last(it)) gen(", "); - } - gen(")"); - - if (n->ret) gen(": "); - core__stringify(n->ret); - core__stringify(n->scope); - } break; - - default: assert(!"Invalid default case"); - } -} - -String core_stringify(Ast *ast) { - core__stringify(ast); - String result = string_flatten(pctx->perm, &pctx->gen); - pctx->gen.reset(); - return result; + +// @todo @cleanup ?? +// :PrinterCleanup +// Instead of relying on global state, this probably should +// use a String_Builder or some kind of Builder context + macros + +CORE_Static String +core_type_to_string(Ast_Type *type) { + + // @todo: use get_name_of_type instead of duplicating the typename dispatch table + switch (type->kind) { + case TYPE_NONE: return ""_s; break; + case TYPE_S64: return "S64"_s; break; + case TYPE_S32: return "S32"_s; break; + case TYPE_S16: return "S16"_s; break; + case TYPE_S8: return "S8"_s; break; + + case TYPE_CHAR: return "char"_s; + case TYPE_UCHAR: return "uchar"_s; + case TYPE_INT: return "int"_s; + case TYPE_UINT: return "uint"_s; + case TYPE_LONG: return "long"_s; + case TYPE_ULONG: return "ulong"_s; + case TYPE_LLONG: return "llong"_s; + case TYPE_ULLONG: return "ullong"_s; + case TYPE_SHORT: return "short"_s; + case TYPE_USHORT: return "ushort"_s; + + case TYPE_U64: return "U64"_s; break; + case TYPE_U32: return "U32"_s; break; + case TYPE_U16: return "U16"_s; break; + case TYPE_U8: return "U8"_s; break; + case TYPE_F32: return "F32"_s; break; + case TYPE_F64: return "F64"_s; break; + case TYPE_BOOL: return "bool"_s; break; + case TYPE_STRING: return "String"_s; break; + case TYPE_VOID: return "void"_s; break; + case TYPE_POINTER: { + String base = core_type_to_string(type->base); + return pctx->fmt("*%Q", base); + } break; + + case TYPE_LAMBDA: { + String_Builder *b = &pctx->helper_builder; + b->addf("("); + Array &args = type->func.args; + For(args) { + String t = core_type_to_string(it); + b->addf("%Q", t); + if (!args.is_last(it)) b->addf(", "); + } + b->addf(")"); + if (type->func.ret) { + String t = core_type_to_string(type->func.ret); + b->addf("%Q", t); + } + String result = string_flatten(pctx->perm, b); + return result; + } break; + + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_ENUM: { + // @fixme: we probably want a string on Ast_Type + // so that we don't have to reach into Ast_Decl + // for Structs Enums etc. + Ast_Decl *decl = (Ast_Decl *)type->ast; + return decl->name.s; + } break; + + case TYPE_ARRAY: { + String base = core_type_to_string(type->base); + return pctx->fmt("[%u]%Q", type->arr.size, base); + } break; + + case TYPE_SLICE: { + String base = core_type_to_string(type->base); + return pctx->fmt("[]%Q", base); + } break; + + case TYPE_TYPE: return "Type"_s; break; + case TYPE_UNTYPED_BOOL: return "UntypedBool"_s; break; + case TYPE_UNTYPED_INT: return "UntypedInt"_s; break; + case TYPE_UNTYPED_FLOAT: return "UntypedFloat"_s; break; + case TYPE_UNTYPED_STRING: return "UntypedString"_s; break; + + case TYPE_COMPLETING: invalid_codepath; break; + case TYPE_INCOMPLETE: invalid_codepath; break; + case TYPE_POLYMORPH: + invalid_codepath; + break; + + invalid_default_case; + } + invalid_return; +} + +void core__stringify(Ast *ast) { + if (!ast) return; + gen("@%u", ast->di); + switch (ast->kind) { + case AST_SCOPE: { + Ast_Scope *n = (Ast_Scope *)ast; + + global_indent += 1; + genln("@%u", n->di); + For(n->decls) { + genln(""); + core__stringify(it); + } + For(n->stmts) { + genln(""); + core__stringify(it); + } + global_indent -= 1; + } break; + + case AST_MODULE: invalid_codepath; break; + case AST_FILE: invalid_codepath; break; + + case AST_IDENT: { + Ast_Atom *n = (Ast_Atom *)ast; + gen("%Q", n->intern_val); + } break; + + case AST_VALUE: { + Ast_Atom *n = (Ast_Atom *)ast; //@todo: proper value + gen("%Q", n->pos->string); + } break; + + case AST_INDEX: { + Ast_Index *n = (Ast_Index *)ast; + core__stringify(n->expr); + gen("["); + core__stringify(n->index); + gen("]"); + } break; + + case AST_UNARY: { + Ast_Unary *n = (Ast_Unary *)ast; + gen("%Q", n->pos->string); + core__stringify(n->expr); + } break; + + case AST_BINARY: { + Ast_Binary *n = (Ast_Binary *)ast; + core__stringify(n->left); + gen("%Q", n->pos->string); + core__stringify(n->right); + } break; + + case AST_CALL_ITEM: { + Ast_Call_Item *n = (Ast_Call_Item *)ast; + if (n->call_flags & CALL_INDEX) { + core__stringify(n->index); + gen(" = "); + } + else if (n->call_flags & CALL_NAME) { + core__stringify(n->name); + gen(" = "); + } + core__stringify(n->item); + + } break; + + case AST_CALL: { + Ast_Call *n = (Ast_Call *)ast; + core__stringify(n->name); + gen("("); + For(n->exprs) { + core__stringify(it); + if (!n->exprs.is_last(it)) gen(","); + } + gen(")"); + } break; + case AST_COMPOUND: { + Ast_Call *n = (Ast_Call *)ast; + core__stringify(n->name); + + gen("{"); + global_indent += 1; + For(n->exprs) { + genln(""); + core__stringify(it); + gen(","); + } + global_indent -= 1; + genln("}"); + } break; + + case AST_TYPE_OF: + gen("typeof"); + goto builtin; + case AST_LENGTH_OF: + gen("LengthOf"); + goto builtin; + case AST_ALIGN_OF: + gen("alignof"); + goto builtin; + case AST_SIZE_OF: + gen("sizeof"); + goto builtin; + case AST_RUNTIME_ASSERT: + gen("Assert"); + goto builtin; + case AST_CONSTANT_ASSERT: + gen("#Assert"); + goto builtin; + builtin : { + Ast_Builtin *n = (Ast_Builtin *)ast; + + gen("("); + core__stringify(n->expr); + gen(")"); + } break; + + case AST_SWITCH: { + Ast_Switch *n = (Ast_Switch *)ast; + + core__stringify(n->value); + For(n->cases) { + core__stringify(it); + } + core__stringify(n->default_scope); + } break; + + case AST_SWITCH_CASE: { + Ast_Switch_Case *n = (Ast_Switch_Case *)ast; + + For(n->labels) { + core__stringify(it); + } + core__stringify(n->scope); + } break; + + case AST_VAR_UNPACK: { + Ast_Var_Unpack *n = (Ast_Var_Unpack *)ast; + For(n->vars) { + core__stringify(it); + if (!n->vars.is_last(it)) gen(","); + } + gen(" = "); + core__stringify(n->expr); + } break; + + case AST_DEFER: { + auto n = (Ast_Defer *)ast; + genln("defer"); + core__stringify(n->scope); + } break; + + case AST_PASS: { + genln("pass"); + } break; + + case AST_CONTINUE: { + genln("continue"); + } break; + + case AST_BREAK: { + genln("break"); + } break; + + case AST_NAMESPACE: { + Ast_Decl *n = (Ast_Decl *)ast; + gen("%Q :: NAMESPACE", n->name); + } break; + + case AST_UNION: + case AST_STRUCT: { + Ast_Decl *n = (Ast_Decl *)ast; + genln("%Q :: %s", n->name, n->kind == AST_STRUCT ? "struct" : "union"); + core__stringify(n->scope); + } break; + + case AST_ENUM: { + Ast_Decl *n = (Ast_Decl *)ast; + genln("%Q :: enum", n->name); + core__stringify(n->scope); + } break; + + case AST_LAMBDA: { + Ast_Decl *n = (Ast_Decl *)ast; + genln("%Q :: ", n->name); + core__stringify(n->lambda); // @cleanup + } break; + + // @cleanup: what is this used for? + case AST_TYPE: { + invalid_codepath; + } break; + + case AST_CONST: { + Ast_Decl *n = (Ast_Decl *)ast; + gen("%Q :: CONST value @todo", n->name); + } break; + + case AST_VAR: { + Ast_Decl *n = (Ast_Decl *)ast; + gen("%Q: %Q", n->name, core_type_to_string(n->type)); + if (n->expr) { + gen(" = "); + core__stringify(n->expr); + } + } break; + + case AST_ARRAY: { + Ast_Array *n = (Ast_Array *)ast; + + gen("["); + core__stringify(n->expr); + gen("]"); + core__stringify(n->base); + } break; + + case AST_FOR: { + Ast_For *n = (Ast_For *)ast; + gen("for "); + core__stringify(n->init); + if (n->cond) gen(", "); + core__stringify(n->cond); + if (n->iter) gen(", "); + core__stringify(n->iter); + core__stringify(n->scope); + } break; + + case AST_IF: { + Ast_If *n = (Ast_If *)ast; + + int i = 0; + For(n->ifs) { + if (i == 0) gen("if "); + else if (it->expr) gen("elif "); + else gen("else "); + + core__stringify(it->expr); + if (it->init) gen(", "); + core__stringify(it->init); + core__stringify(it->scope); + + i += 1; + } + } break; + + case AST_RETURN: { + Ast_Return *n = (Ast_Return *)ast; + gen("return "); + core__stringify(n->expr); + } break; + + case AST_LAMBDA_EXPR: { + Ast_Lambda *n = (Ast_Lambda *)ast; + + gen("("); + For(n->args) { + core__stringify(it); + if (!n->args.is_last(it)) gen(", "); + } + gen(")"); + + if (n->ret) gen(": "); + core__stringify(n->ret); + core__stringify(n->scope); + } break; + + default: assert(!"Invalid default case"); + } +} + +String core_stringify(Ast *ast) { + core__stringify(ast); + String result = string_flatten(pctx->perm, &pctx->gen); + pctx->gen.reset(); + return result; } \ No newline at end of file diff --git a/core_typechecking.cpp b/src/language/core_typechecking.cpp similarity index 97% rename from core_typechecking.cpp rename to src/language/core_typechecking.cpp index a954398..d82aa01 100644 --- a/core_typechecking.cpp +++ b/src/language/core_typechecking.cpp @@ -1,1958 +1,1958 @@ - -CORE_Static Resolve_Flag -inherit_flag(Resolve_Flag flag, B32 ast_can_be_null) { - unset_flag(flag, AST_CAN_BE_NULL); - set_flag(flag, ast_can_be_null); - return flag; -} - -//----------------------------------------------------------------------------- -// Operands -//----------------------------------------------------------------------------- -CORE_Static Operand -operand(Ast_Decl *decl) { - Operand result = {}; - result.type = decl->type; - result.is_const = decl->kind != AST_VAR ? true : false; - result.is_lvalue = decl->kind == AST_CONST ? false : true; // Cant assign to const values - result.value = decl->value; - result.resolved_decl = decl; - return result; -} - -static Operand -operand_type(Ast_Type *type) { - Operand result = {}; - result.type = pctx->type_type; - result.is_const = true; - result.is_lvalue = false; - result.type_val = type; - return result; -} - -CORE_Static Operand -operand_int(BigInt big_int) { - Operand result = {}; - result.type = pctx->untyped_int; - result.big_int_val = bigint_copy(pctx->perm, &big_int); - result.is_const = true; - result.is_lvalue = false; - return result; -} - -CORE_Static Operand -operand_str(Intern_String intern_val) { - Operand result = {}; - result.type = pctx->type_string; - result.intern_val = intern_val; - result.is_const = true; - result.is_lvalue = false; - return result; -} - -CORE_Static Operand -operand_lambda(Ast_Type *type) { - Operand result = {}; - result.type = type; - result.is_const = true; - result.is_lvalue = false; - return result; -} - -CORE_Static Operand -operand_const_rvalue(Value value) { - Operand result = {}; - result.is_const = true; - result.value = value; - return result; -} - -CORE_Static Operand -operand_lvalue_set_flag_on_node(Ast_Type *type, Ast *ast) { - Operand result = {}; - result.type = type; - result.is_const = false; - result.is_lvalue = true; - set_flag(ast->flags, AST_IS_LVALUE); - return result; -} - -CORE_Static Operand -operand_rvalue(Ast_Type *type) { - Operand result = {}; - result.type = type; - result.is_const = false; - result.is_lvalue = false; - return result; -} - -CORE_Static Ast_Type * -get_default_type_from_untyped(Ast_Type *type) { - switch (type->kind) { - case TYPE_UNTYPED_INT: return pctx->type_int; break; - case TYPE_UNTYPED_BOOL: return pctx->type_bool; break; - case TYPE_UNTYPED_STRING: return pctx->type_string; break; - case TYPE_UNTYPED_FLOAT: return pctx->type_f32; break; - default: invalid_codepath; - } - return 0; -} - -CORE_Static void -try_converting_untyped_to_default_type(Value *op) { - if (is_untyped(op->type)) { - op->type = get_default_type_from_untyped(op->type); - } -} - -CORE_Static void -try_converting_untyped_to_default_type(Operand *op) { - try_converting_untyped_to_default_type(&op->value); -} - -CORE_Static void -check_value_bounds(Token *pos, Value *a) { - if (!is_int(a->type)) return; - - Scoped_Arena scratch(pctx->scratch); - if (!bigint_fits_in_bits(&a->big_int_val, a->type->size * 8, is_signed_int(a->type))) { - const char *string = bigint_to_error_string(scratch.arena, &a->big_int_val, 10); - compiler_error(pos, "Value %s doesn't fit in type %Q", string, typestring(a->type)); - } -} - -CORE_Static void -convert_untyped_to_typed(Token *pos, Value *a, Ast_Type *new_type) { - assert(new_type); - if (a->type == 0) return; - if (is_typed(a->type)) return; - - if (is_any(new_type)) - new_type = get_default_type_from_untyped(a->type); - else if (is_int(a->type) && is_int(new_type)) - assert(a->type == pctx->untyped_int); - else if (is_int(a->type) && is_enum(new_type)) - ; - else if (is_int(a->type) && is_float(new_type)) - a->f64_val = bigint_as_float(&a->big_int_val); // @leak bigint - else if (is_int(a->type) && is_pointer(new_type)) - ; - else if (is_float(a->type) && is_float(new_type)) - ; // nothing to do - else if (is_bool(a->type) && is_bool(new_type)) - ; // nothing to do - else if (is_string(a->type) && is_string(new_type)) - ; // nothing to do - else compiler_error(pos, "Type mismatch when converting from %Q to %Q", typestring(a->type), typestring(new_type)); - - a->type = new_type; - check_value_bounds(pos, a); -} - -CORE_Static Ast_Type * -convert_untyped_to_typed(Token *pos, Ast_Type *type, Ast_Type *new_type) { - Value value = {}; - value.type = type; - convert_untyped_to_typed(pos, &value, new_type); - return value.type; -} - -CORE_Static void -make_sure_types_are_compatible_for_constant_evaluation(Token *pos, Value *a, Value *b) { - if ((is_pointer(a->type) && is_int(b->type)) || (is_pointer(b->type) && is_int(a->type))) { - return; - } - else if (is_pointer(a->type) && is_pointer(b->type)) { - return; - } - else if (is_typed(a->type) && is_typed(b->type)) { - if (a->type != b->type) { - compiler_error(pos, "Type mismatch in make_sure_types_are_compatible_for_constant_evaluation - left: %Q right: %Q", typestring(a->type), typestring(b->type)); - } - } - - if (is_untyped(a->type) && is_typed(b->type)) { - assert(is_typed(b->type)); - convert_untyped_to_typed(pos, a, b->type); - } - else if (is_typed(a->type) && is_untyped(b->type)) { - assert(is_typed(a->type)); - convert_untyped_to_typed(pos, b, a->type); - } - else if (is_int(a->type) && is_float(b->type)) { - *a = value_float(a->big_int_val); - } - else if (is_float(a->type) && is_int(b->type)) { - *b = value_float(b->big_int_val); - } -} - -CORE_Static Value -compare_values(Token *pos, Token_Kind op, Value a, Value b, bool is_const) { - if (is_enum(a.type) && (a.type == b.type)) - goto skip_eval; - if (a.type->kind == TYPE_TYPE && b.type->kind == TYPE_TYPE) - goto skip_eval; - if (!(is_numeric(a.type) && is_numeric(b.type))) - compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); - - make_sure_types_are_compatible_for_constant_evaluation(pos, &a, &b); - -skip_eval: - B32 result = 0; - if (is_const) { - switch (a.type->kind) { - case TYPE_TYPE: { - switch (op) { - case TK_Equals: result = a.type_val->type_id == b.type_val->type_id; break; - case TK_NotEquals: result = a.type_val->type_id != b.type_val->type_id; break; - default: goto failure; - } - } break; - CASE_INT : { - CmpRes cmp = bigint_cmp(&a.big_int_val, &b.big_int_val); - switch (op) { - case TK_LesserThenOrEqual: result = (cmp == CMP_LT) || (cmp == CMP_EQ); break; - case TK_GreaterThenOrEqual: result = (cmp == CMP_GT) || (cmp == CMP_EQ); break; - case TK_GreaterThen: result = cmp == CMP_GT; break; - case TK_LesserThen: result = cmp == CMP_LT; break; - case TK_Equals: result = cmp == CMP_EQ; break; - case TK_NotEquals: result = cmp != CMP_EQ; break; - default: goto failure; - } - } break; - CASE_BOOL : { - switch (op) { - case TK_Equals: result = a.bool_val == b.bool_val; break; - case TK_NotEquals: result = a.bool_val != b.bool_val; break; - default: goto failure; - } - } break; - CASE_FLOAT : { - switch (op) { - case TK_LesserThenOrEqual: result = a.f64_val <= b.f64_val; break; - case TK_GreaterThenOrEqual: result = a.f64_val >= b.f64_val; break; - case TK_GreaterThen: result = a.f64_val > b.f64_val; break; - case TK_LesserThen: result = a.f64_val < b.f64_val; break; - case TK_Equals: result = a.f64_val == b.f64_val; break; - case TK_NotEquals: result = a.f64_val != b.f64_val; break; - default: goto failure; - } - } break; - - CASE_STRING : { - goto failure; - } break; - - default: { - failure: - compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); - } - } - } - - return value_bool(result); -} - -CORE_Static Value -eval_binary(Token *pos, Token_Kind op, Value a, Value b, bool is_const) { - if (token_is_compare(op)) - return compare_values(pos, op, a, b, is_const); - - if (!(is_numeric(a.type) && is_numeric(b.type))) - compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); - - make_sure_types_are_compatible_for_constant_evaluation(pos, &a, &b); - - // @todo: Add compiler error to the invalid switch cases !!! - Value result = {}; - result.type = a.type; - if (is_const) { - switch (a.type->kind) { - CASE_INT : { - switch (op) { - case TK_BitXor: bigint_xor(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_BitAnd: bigint_and(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_BitOr: bigint_or(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Add: bigint_add(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Sub: bigint_sub(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Mul: bigint_mul(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Div: bigint_div_trunc(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_Mod: bigint_mod(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_LeftShift: bigint_shl(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; - case TK_RightShift: - bigint_shr(&result.big_int_val, &a.big_int_val, &b.big_int_val); - break; - invalid_default_case; - } - } break; - CASE_BOOL : { - switch (op) { - case TK_And: result.bool_val = a.bool_val && b.bool_val; break; - case TK_Or: - result.bool_val = a.bool_val || b.bool_val; - break; - invalid_default_case; - } - } break; - CASE_FLOAT : { - switch (op) { - case TK_Add: result.f64_val = a.f64_val + b.f64_val; break; - case TK_Sub: result.f64_val = a.f64_val - b.f64_val; break; - case TK_Mul: result.f64_val = a.f64_val * b.f64_val; break; - case TK_Div: - result.f64_val = a.f64_val / b.f64_val; - break; - invalid_default_case; - } - } break; - CASE_STRING : { - invalid_codepath; - } break; - invalid_default_case; - } - } - - return result; -} - -CORE_Static S64 -digit_count(const Value *a) { - S64 digit_count = a->big_int_val.digit_count * 64; - if (is_typed(a->type)) { - digit_count = a->type->size * 8; - } - return digit_count; -} - -CORE_Static void -eval_unary(Token *pos, Token_Kind op, Operand *operand) { - Value *a = &operand->value; - Ast_Type *type = a->type; - if (!is_numeric(type)) - compiler_error(pos, "Unary [%s] cant be applied to value of type %Q", name(op), typestring(type)); - - if (op == TK_Not) - a->type = pctx->untyped_bool; - - if (op == TK_Increment || op == TK_Decrement || op == TK_PostIncrement || op == TK_PostDecrement) - if (!operand->is_lvalue) - compiler_error(pos, "Unary [%s] requires an assignable value(lvalue)", name(op)); - - if (!operand->is_const) - return; - - BigInt result = {}; - switch (op) { - case TK_Add: { - } break; - case TK_Sub: { - switch (type->kind) { - CASE_INT : { - bigint_negate(&result, &a->big_int_val); - a->big_int_val = result; - } break; - CASE_FLOAT : { - a->f64_val = -a->f64_val; - } break; - default: goto failure; - } - } break; - case TK_Neg: { - switch (type->kind) { - CASE_SINT: - case TYPE_UNTYPED_INT: - bigint_not(&result, &a->big_int_val, digit_count(a), 1); - break; - CASE_UINT: - bigint_not(&result, &a->big_int_val, digit_count(a), 0); - break; - default: goto failure; - } - a->big_int_val = result; - } break; - case TK_Not: { - switch (type->kind) { - CASE_INT : { - if (CMP_EQ == bigint_cmp_zero(&a->big_int_val)) - a->bool_val = 1; - a->bool_val = 0; - } break; - CASE_FLOAT: - a->bool_val = !a->f64_val; - break; - CASE_BOOL: - a->bool_val = !a->bool_val; - break; - default: goto failure; - } - } break; - default: - failure: - compiler_error(pos, "Constant application of unary %s on values of type %Q is not allowed", name(op), typestring(a->type)); - } -} - -CORE_Static void -make_sure_value_is_compatible_with_type(Token *pos, Operand *expr, Ast_Type *type, Typecheck_Flag debug_flag) { - if (type == expr->type) { - assert(type); - assert(expr->type); - return; - } - - if (!type || type->kind == TYPE_VARGS) { - assert(is_flag_set(debug_flag, TYPE_CAN_BE_NULL)); - assert(expr->type); - try_converting_untyped_to_default_type(expr); - } - else if (!expr->type) { - assert(is_flag_set(debug_flag, EXPR_CAN_BE_NULL)); - assert(type); - expr->type = type; - } - else if (is_any(type)) { - expr->type = type; - } - else if (is_void_pointer(type) && is_pointer(expr->type)) { - expr->type = type; - } - else if (is_pointer(type) && is_void_pointer(expr->type)) { - expr->type = type; - } - else if (is_untyped(expr->type)) { - convert_untyped_to_typed(pos, &expr->value, type); - } - else if (is_slice(type) && is_array(expr->type)) { - if (type->arr.slice_hash == expr->type->arr.slice_hash) - expr->type = type; - } - - if (type && expr->type != type) { - String expr_type_string = typestring(expr->type); - String type_string = typestring(type); - compiler_error(pos, "Assigning but incompatible types, expression: %Q expected var type: %Q", expr_type_string, type_string); - } - - type_complete(expr->type); - check_value_bounds(pos, &expr->value); - assert(expr->type); -} - -#define rewrite_into_const(ast, T, s) _rewrite_into_const(ast, sizeof(T), s) -CORE_Static void _rewrite_into_const(Ast *node, U64 ast_size, Value value) { - auto ast = (Ast_Atom *)node; - assert(ast_size >= sizeof(Ast_Atom)); - set_flag(ast->flags, AST_ATOM); - if (value.type && is_lambda(value.type)) { - ast->kind = AST_IDENT; - ast->value = value; - } - else { - ast->kind = AST_VALUE; - ast->value = value; - ast->resolved_type = value.type; - } -} - -CORE_Static void -inside_scope_search(Scope_Search *search, Ast_Scope *scope, int level) { - if (scope->visit_id == search->scope_visit_id) return; - scope->visit_id = search->scope_visit_id; - - // Search for declarations in current scope - For2(scope->decls, decl) { - if (decl->name == search->name) { - search->results.add(decl); - if (search->exit_on_find) { - return; - } - } - } - - // Search for declarations in imported implicitly scopes - // We dont want to descent into foreign implicit imports - // so we cap it with level and then we need to make sure that we - // descent into module 1 level away and it's files but no more then that - For(scope->implicit_imports) { - if (level > 0 && it->kind == AST_MODULE) continue; - inside_scope_search(search, it, level + 1); - - if (search->exit_on_find && search->results.len) { - return; - } - } -} - -CORE_Static void -scope_search(Scope_Search *search) { - // Climb the scope tree and search each scope in module and also - // search in implicitly imported scopes - For2(search->scopes, scope) { - for (Ast_Scope *it = scope; it; it = it->parent_scope) { - inside_scope_search(search, it, 0); - - if (search->exit_on_find && search->results.len) { - return; - } - - if (search->search_only_current_scope) { - return; - } - } - inside_scope_search(search, scope->module, 0); - } -} - -CORE_Static Scope_Search -make_scope_search(Allocator *arena, Ast_Scope *scope, Intern_String name) { - Scope_Search result = {}; - result.results.allocator = arena; - result.name = name; - result.scope_visit_id = ++pctx->scope_visit_id; - result.scopes = {arena}; - result.scopes.add(scope); - result.exit_on_find = false; - return result; -} - -CORE_Static Ast_Decl * -search_for_single_decl(Ast_Scope *scope, Intern_String name, bool error_if_no_matches = true) { - Scoped_Arena scratch(pctx->scratch); - - Scope_Search search = make_scope_search(scratch.arena, scope, name); - scope_search(&search); - - if (search.results.len == 0) { - if (error_if_no_matches) compiler_error(0, "Unidentified name [%s]", name.str); - return 0; - } - if (search.results.len > 1) { - compiler_error(search.results.data[0]->pos, search.results.data[1]->pos, "Found multiple definitions of name [%s]", name.str); - } - - return search.results.data[0]; -} - -CORE_Static Ast_Decl * -resolve_name(Ast_Scope *scope, Token *pos, Intern_String name, Search_Flag search_flags) { - Scoped_Arena scratch(pctx->scratch); - Scope_Search search = make_scope_search(scratch.arena, scope, name); - search.search_only_current_scope = search_flags & SEARCH_ONLY_CURRENT_SCOPE; - search.exit_on_find = true; - scope_search(&search); - - if (search.results.len == 0) { - compiler_error(pos, "Unidentified name [%s]", name.str); - } - - if (is_flag_set(search.results[0]->flags, AST_OPERATOR_OVERLOAD)) { - compiler_error(pos, "Internal compiler error: resolve_name got an overload"); - } - - Ast_Decl *decl = *search.results.data; - resolve_decl(decl); - return decl; -} - -CORE_Static Ast_Decl * -resolve_operator_overload(Ast_Scope *scope, Ast_Type *left, Ast_Type *right, Token *pos, Token_Kind op, U64 argument_hash) { - Ast_Operator_Info *op_info = get_operator_info(op); - if (op_info == 0) return 0; - - // Search for all possible candidates in three scopes - // The current module, left type definition module, right type definition module - Scoped_Arena scratch(pctx->scratch); - Scope_Search search = make_scope_search(scratch.arena, scope, op_info->op); - if (left->ast && left->ast->parent_scope) search.scopes.add(left->ast->parent_scope); - if (right && right->ast && right->ast->parent_scope) search.scopes.add(right->ast->parent_scope); - search.exit_on_find = false; - scope_search(&search); - - // Resolve them until we hit a match - Array matching_ops = {scratch.arena}; - For(search.results) { - resolve_decl(it); - if (it->type->func.hash_without_ret == argument_hash) { - matching_ops.add(it); - } - } - - if (matching_ops.len > 1) { - compiler_error(pos, "Found multiple matching operator overloads for [%Q]", op_info->op); - } - - if (matching_ops.len == 1) { - return matching_ops.data[0]; - } - - return 0; -} - -CORE_Static void -insert_into_scope(Ast_Scope *scope, Ast_Decl *decl) { - // This function is called when resolving statements - // inside code blocks, inserting lambda arguments into scope - // - // It's also called when scanning top level declarations of a module - // as such we probably don't want to call any resolve stuff here - Scoped_Arena scratch(pctx->scratch); - Scope_Search search = make_scope_search(scratch.arena, scope, decl->name); - search.search_only_current_scope = true; - scope_search(&search); - if (search.results.len != 0) { - if (!is_flag_set(decl->flags, AST_OPERATOR_OVERLOAD)) { - compiler_error(search.results.data[0]->pos, decl->pos, "[%s] is already defined", decl->name.str); - } - } - - add(pctx->perm, &scope->decls, decl); -} - -// -// Booleans are special in that untyped boolean appears from every comparison -// and children of that comparison don't have to be booleans so we have to -// propagate values of resolved children to each other -// -// int == untyped_int => bool -// need to propagate int to untyped_int -// -CORE_Static void -try_propagating_resolved_type_to_untyped_literals(Ast *ast, Ast_Type *type, Ast_Type *additional_not_bool_type) { - if (!type) compiler_error(ast->pos, "Internal compiler error: Type passed to try_propagating_resolved_type_to_untyped_literals is null"); - if (!ast) return; - if (is_untyped(type)) return; - - // @note: - // Normal untyped should only be 1 deep cause we make sure to colapse all constants - // and only constants can be untyped - if (!is_bool(type)) { - if (ast->kind != AST_VALUE) return; - - auto node = (Ast_Atom *)ast; - convert_untyped_to_typed(ast->pos, &node->value, type); - - // This only is required when we have arrays of Any type. - // That is because we are folding all the constants and there - // the resolved_type is set - node->resolved_type = node->value.type; - return; - } - - // Boolean special case - switch (ast->kind) { - CASE(IDENT, Atom) { - type = additional_not_bool_type ? additional_not_bool_type : type; - convert_untyped_to_typed(ast->pos, &node->value, type); - BREAK(); - } - - CASE(VALUE, Atom) { - type = additional_not_bool_type ? additional_not_bool_type : type; - convert_untyped_to_typed(ast->pos, &node->value, type); - BREAK(); - } - - CASE(BINARY, Binary) { - node->resolved_type = convert_untyped_to_typed(ast->pos, node->resolved_type, type); - try_propagating_resolved_type_to_untyped_literals(node->left, type, node->right->resolved_type); - try_propagating_resolved_type_to_untyped_literals(node->right, type, node->left->resolved_type); - BREAK(); - } - - CASE(CALL, Call) { - if (is_untyped(node->resolved_type)) - compiler_error(node->pos, "Internal compiler error: Resolved type of a call is marked as untyped"); - BREAK(); - } - - CASE(UNARY, Unary) { - node->resolved_type = convert_untyped_to_typed(ast->pos, node->resolved_type, type); - try_propagating_resolved_type_to_untyped_literals(node->expr, type); - BREAK(); - } - - default: { - } - } -} - -CORE_Static Ast_Type * -resolve_typespec(Ast_Expr *ast, Resolve_Flag flags) { - if (!ast && is_flag_set(flags, AST_CAN_BE_NULL)) - return 0; - - Operand resolved = resolve_expr(ast, flags | RESOLVE_TYPESPEC, 0, 0); - if (is_flag_set(flags, RESOLVE_TYPESPEC_COMPLETE)) - type_complete(resolved.type_val); - if (resolved.type != pctx->type_type) - compiler_error(ast->pos, "Expected [Type] got instead %Q", typestring(resolved.type)); - return resolved.type_val; -} - -CORE_Static Operand -resolve_and_require_bool(const char *error, Ast_Expr *expr, Resolve_Flag flags) { - if (!expr) { - if (flags == AST_CAN_BE_NULL) - return {}; - else compiler_error(0, "Compiler error: Null expression"); - } - - Operand op = resolve_expr(expr, flags, 0, 0); - if (!is_bool(op.type)) { - compiler_error(expr->pos, "Expected type [Bool] got instead type %Q :: %s", typestring(op.type), error); - } - - return op; -} - -CORE_Static Operand -require_const_int(Ast_Expr *expr, Resolve_Flag flags) { - Operand op = resolve_expr(expr, flags, 0, 0); - - if (expr == 0 && flags) - return op; - else if (expr == 0) - compiler_error(expr->pos, "This field cannot be null"); - - if (!op.is_const) - compiler_error(expr->pos, "Expected a const value"); - if (!(is_int(op.type) || is_enum(op.type))) - compiler_error(expr->pos, "Expected a constant integer got instead %Q", typestring(op.type)); - - return op; -} - -// ret is return value passed down the stack to check if type matches -CORE_Static void resolve_stmt(Ast *ast, Ast_Type *ret) { - if (!ast) return; - assert(ast->parent_scope->kind == AST_SCOPE); - if (ast->flags & AST_COMPILER_BREAKPOINT) Breakpoint; - - switch (ast->kind) { - CASE(RETURN, Return) { // @todo: need to check if all paths return a value - Ast_Type *value_type = pctx->type_void; - if (node->expr) { - Operand op = resolve_expr(node->expr, AST_CAN_BE_NULL, ret, 0); - convert_untyped_to_typed(node->pos, &op.value, ret); - value_type = op.type; - } - - if (value_type != ret) compiler_error(node->pos, "Return statement has different type then returned value, expecting: %Q got instead %Q", typestring(ret), typestring(value_type)); - node->resolved_type = ret; - - if (node->expr) try_propagating_resolved_type_to_untyped_literals(node->expr, ret); - - BREAK(); - } - - case AST_LAMBDA: - case AST_VAR: - CASE(CONST, Decl) { - resolve_decl(node); - insert_into_scope(node->parent_scope, node); - BREAK(); - } - - case AST_COMPILER_BREAKPOINT_STMT: { - __debugbreak(); - } break; - - case AST_LABEL: { - auto n = (Ast_Label *)ast; - insert_into_scope(n->parent_scope, n); - For(n->scope->stmts) { - resolve_stmt(it, ret); - } - } break; - - case AST_GOTO: { - auto n = (Ast_Goto *)ast; - auto decl = (Ast_Label *)search_for_single_decl(n->parent_scope, n->label); - if (!decl) compiler_error(n->pos, "Goto label is unidentified"); - if (decl->kind != AST_LABEL) compiler_error(n->pos, "Trying to goto identifier that is not a label"); - if (decl->enable_goto == false) compiler_error(decl->pos, "Label doesn't have goto enabled, add ':' at the end"); - } break; - - case AST_DEFER: { - auto n = (Ast_Defer *)ast; - For(n->scope->stmts) { - resolve_stmt(it, ret); - } - } break; - - case AST_CONTINUE: - case AST_BREAK: - case AST_PASS: { - - } break; - - CASE(RUNTIME_ASSERT, Builtin) { - resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); - try_propagating_resolved_type_to_untyped_literals(node->expr, pctx->type_bool); - BREAK(); - } - - CASE(CONSTANT_ASSERT, Builtin) { - Operand op = resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); - if (!op.is_const) compiler_error(node->pos, "#Assert expression required to be constant"); - if (op.bool_val == false) { - compiler_error(node->pos, "#Assert condition not met :: %.*s", (int)node->assert_message.len, node->assert_message.str); - } - BREAK(); - } - - CASE(FOR, For) { - if (node->init && node->cond == 0 && node->iter == 0) { - if (!is_flag_set(node->init->flags, AST_STMT)) { - node->cond = node->init; - node->init = 0; - } - } - - resolve_expr(node->init, AST_CAN_BE_NULL, 0, 0); - Operand op = resolve_expr(node->cond, AST_CAN_BE_NULL, 0, 0); - if (node->cond) { - if ((!node->init && !node->iter) && (is_array(op.type) || is_slice(op.type))) { - node->is_array_traversal = true; - if (is_slice(op.type)) node->is_also_slice_traversal = true; - Ast_Decl *var = ast_new(Ast_Decl, AST_VAR, node->cond->pos, AST_DECL); - var->state = DECL_RESOLVED; - var->type = type_pointer(op.type->base); - var->name = pctx->intern_it; - var->unique_name = var->name; - insert_into_scope(node->scope, var); - node->array_traversal_var = var; - } - else if (!is_bool(op.type)) { - compiler_error(node->pos, "Invalid type of for loop condition %Q, required [Bool]", typestring(op.type)); - } - else { - try_propagating_resolved_type_to_untyped_literals(node->cond, pctx->type_bool); - } - } - - resolve_expr(node->iter, AST_CAN_BE_NULL, 0, 0); - For(node->scope->stmts) resolve_stmt(it, ret); - BREAK(); - } - - // @todo: maybe add else kind ?? and then make sure other then else are AST_CANT_BE_NULL - CASE(IF, If) { - For(node->ifs) { - resolve_stmt(it->init, ret); - resolve_and_require_bool("Conditional in a if condition", it->expr, AST_CAN_BE_NULL); - try_propagating_resolved_type_to_untyped_literals(it->expr, pctx->type_bool); - For2(it->scope->stmts, jt) - resolve_stmt(jt, ret); - } - BREAK(); - } - - CASE(SWITCH, Switch) { - Operand base = resolve_expr(node->value, AST_CANT_BE_NULL, 0, 0); - if (!is_int(base.type) && !is_enum(base.type)) - compiler_error(node->pos, "You can only switch on integer values(enums included), the type of expression in switch statement is instead %Q", typestring(base.type)); - - try_converting_untyped_to_default_type(&base.value); - try_propagating_resolved_type_to_untyped_literals(node->value, base.type); - For(node->cases) { - For2(it->labels, label) { - Operand op = resolve_expr(label, AST_CANT_BE_NULL, 0, 0); - if (!op.is_const) compiler_error(label->pos, "Switch label required to be constant"); - make_sure_value_is_compatible_with_type(label->pos, &op, base.type, TYPE_AND_EXPR_REQUIRED); - try_propagating_resolved_type_to_untyped_literals(label, base.type); - } - - For2(it->scope->stmts, stmt) - resolve_stmt(stmt, ret); - } - if (node->default_scope) - For2(node->default_scope->stmts, stmt) - resolve_stmt(stmt, ret); - - BREAK(); - } - - CASE(VAR_UNPACK, Var_Unpack) { - Operand expr_op = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, 0); - if (!is_struct_union(expr_op.type)) - compiler_error(node->pos, "Expected expression to have either [Struct] or [Union] type"); - if (expr_op.type->agg.members.len != node->vars.len) - compiler_error(node->pos, "Different count of return values and assigning values"); - node->resolved_type = expr_op.type; - - For(node->vars) { - S64 index = node->vars.get_index(it); - Ast_Resolved_Member *type = expr_op.type->agg.members.data + index; - it->type = type->type; - resolve_decl(it); - insert_into_scope(node->parent_scope, it); - } - BREAK(); - } - - default: { - if (is_flag_set(ast->flags, AST_EXPR)) { - assert(is_flag_set(ast->flags, AST_STMT)); - resolve_expr((Ast_Expr *)ast, AST_CANT_BE_NULL, 0, 0); - } - else invalid_codepath; - } - } -} - -CORE_Static Ast_Type * -resolve_lambda_type(Ast_Lambda *lambda) { - Scoped_Arena scratch(pctx->scratch); - Array args = {scratch.arena}; - Ast_Type *ret = resolve_typespec(lambda->ret, AST_CAN_BE_NULL); - if (!ret) ret = pctx->type_void; - - For(lambda->args) { - if (it->name == pctx->intern("..."_s)) { - it->type = pctx->type_vargs; - - For2(lambda->args, arg) { - if (arg->expr != 0) compiler_error(arg->pos, "Default values in variadic functions are not allowed"); - } - } - else { - Ast_Type *type = resolve_typespec(it->typespec, AST_CANT_BE_NULL); - Operand default_value = resolve_expr(it->expr, AST_CAN_BE_NULL, type, 0); - make_sure_value_is_compatible_with_type(it->pos, &default_value, type, EXPR_CAN_BE_NULL); - - it->type = type; - try_propagating_resolved_type_to_untyped_literals(it->expr, it->type); - } - - args.add(it->type); - } - - return type_lambda(lambda, ret, args); -} - -CORE_Static void -try_resolving_lambda_scope(Operand *op, Ast_Lambda *lambda, Ast_Type *lambda_type) { - if (lambda->scope) { - For(lambda->args) { - insert_into_scope(lambda->scope, it); - } - For(lambda->scope->stmts) { - resolve_stmt(it, lambda_type->func.ret); - } - - *op = operand_lambda(lambda_type); - - // - // We remove all declarations to cleanup the scope - // for iteration - // - // :NestedDeclarations - // If I want to implement nested declarations - // then probably this will need to get reverted - free_all_nodes(&lambda->scope->decls); - } - else if (is_flag_set(lambda->flags, AST_FOREIGN)) { - *op = operand_lambda(lambda_type); - } -} - -CORE_Static Operand -resolve_cast(Ast_Binary *node) { - Operand expr = resolve_expr(node->left, AST_CANT_BE_NULL, 0, 0); - Ast_Type *type = resolve_typespec(node->right, AST_CANT_BE_NULL); - Ast_Type *original_type = expr.type; - node->before_type = expr.type; - - switch (expr.type->kind) { - case TYPE_TYPE: { - if (is_int(type)) { - expr.type = type; - } - else goto failure; - } break; - case TYPE_ENUM: { - if (is_int(type)) - expr.type = type; - else goto failure; - } break; - case TYPE_ARRAY: { - Ast_Type *array_base = expr.type->base; - Ast_Type *pointer_base = type_pointer(array_base); - if (pointer_base == type) { - expr.type = pointer_base; - } - else goto failure; - } break; - case TYPE_POINTER: { - if (is_pointer(type)) - expr = operand_rvalue(type); - else if (is_int(type)) - expr = operand_rvalue(type); - else goto failure; - } break; - CASE_UNTYPED : { - convert_untyped_to_typed(node->pos, &expr.value, type); - } break; - CASE_UINT: - CASE_SINT : { - if (is_int(type)) - expr.type = type; - else if (is_float(type)) { - expr.value.type = type; - if (expr.is_const) expr.value.f64_val = bigint_as_float(&expr.big_int_val); // @leak - } - else goto failure; - } break; - case TYPE_F32: - case TYPE_F64: { - if (is_float(type)) { - expr.type = type; - } - else if (is_int(type)) { - if (expr.is_const) expr.value.big_int_val = bigint_s64((S64)expr.value.f64_val); // @todo: What to do here??? - expr.type = type; - } - else goto failure; - } break; - default: - failure: - compiler_error(node->pos, "Failed to cast from %Q to %Q", typestring(expr.type), typestring(type)); - ; - } - - assert(original_type != type ? expr.type == type : 1); - if (expr.is_const) { - check_value_bounds(node->pos, &expr.value); - rewrite_into_const(node, Ast_Binary, expr.value); - } - - node->resolved_type = expr.type; - if (!expr.is_const) - try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type); - return expr; -} - -CORE_Static void -resolve_compound_array(Ast_Call *node, Ast_Type *type) { - Ast_Type *item_type = type->arr.base; - S64 size = type->arr.size; - - if (is_array(type) && node->exprs.len > size) - compiler_error(node->pos, "Too many items in compound expression, expected: %lld got: %lld", size, node->exprs.len); - - int default_counter = 0; - For(node->exprs) { - if (is_flag_set(it->call_flags, CALL_NAME)) - compiler_error(it->pos, "Arrays can't have named compound expression arguments"); - - if (is_flag_set(it->call_flags, CALL_INDEX)) { - default_counter = -1; - Operand op = require_const_int(it->index, AST_CANT_BE_NULL); - S64 i = bigint_as_signed(&op.value.big_int_val); // @todo: what happens when big num is here??? - - if (i >= size) - compiler_error(it->pos, "Index %lld is out of range of array of size %lld", i, size); - it->resolved_index = (S32)i; - } - else { - if (default_counter == -1) - compiler_error(it->pos, "Mixing expicit indexes and default is illegal"); - it->resolved_index = (S32)default_counter; - } - - Operand item = resolve_expr(it->item, AST_CANT_BE_NULL, item_type, 0); - make_sure_value_is_compatible_with_type(it->pos, &item, item_type, TYPE_AND_EXPR_REQUIRED); - it->resolved_type = item_type; - try_propagating_resolved_type_to_untyped_literals(it->item, it->resolved_type); - default_counter += 1; - } -} - -CORE_Static void -resolve_compound_struct(Ast_Call *node, Ast_Type *type) { - if (node->exprs.len != 1 && is_union(type)) compiler_error(node->pos, "Too many union initializers. Only 1 named initializer required."); - - int counter = 0; - bool named_field_appeared = false; - For(node->exprs) { - if (is_union(type) && !is_flag_set(it->call_flags, CALL_NAME)) compiler_error(it->pos, "Unions can only be initialized using named fields"); - if (is_flag_set(it->call_flags, CALL_INDEX)) compiler_error(it->pos, "Index specifier in a struct compound expression is not legal"); - if (is_flag_set(it->call_flags, CALL_NAME)) named_field_appeared = true; - if (!is_flag_set(it->call_flags, CALL_NAME) && named_field_appeared) compiler_error(it->pos, "Mixing positional and named fields in compound expressions is illegal"); - if (counter >= type->agg.members.len) compiler_error(it->pos, "Too many struct initializers"); - counter += 1; - } - - int default_counter = 0; - For(node->exprs) { - Ast_Type *item_type = 0; - if (is_flag_set(it->call_flags, CALL_NAME)) { - For2(type->agg.members, m) { - if (it->name->intern_val == m.name) { - it->resolved_name = m.name; - it->resolved_index = (S32)type->agg.members.get_index(m); - - if (m.type == pctx->type_type) { - item_type = m.type_val; - } - else { - item_type = m.type; - } - - if (m.visited) { - compiler_error(it->pos, "Field already initialized"); - } - else { - m.visited = true; - } - break; - } - } - if (!item_type) compiler_error(it->pos, "No member with that name"); - } - else { - Ast_Resolved_Member *m = &type->agg.members[default_counter]; - it->resolved_name = m->name; - it->resolved_index = (S32)default_counter; - m->visited = true; - - if (m->type == pctx->type_type && m->type_val) { - item_type = m->type_val; - } - else { - item_type = m->type; - } - - default_counter += 1; - } - - assert(item_type); - Operand item = resolve_expr(it->item, AST_CANT_BE_NULL, item_type, 0); - make_sure_value_is_compatible_with_type(it->pos, &item, item_type, TYPE_AND_EXPR_REQUIRED); - - it->resolved_type = item_type; - try_propagating_resolved_type_to_untyped_literals(it->item, it->resolved_type); - } - - For(type->agg.members) it.visited = false; -} - -CORE_Static Ast_Expr * -unpack_ast_call_expr_for_builtin(Ast_Call *call) { - if (call->exprs.len != 1) { - compiler_error(call->pos, "Expected exactly 1 argument inside a builtin function call got instead %d", (int)call->exprs.len); - } - return call->exprs[0]->item; -} - -CORE_Static bool -expr_atom_is_equal_intern(Ast_Expr *expr, Intern_String intern) { - assert(expr->kind == AST_IDENT || expr->kind == AST_BINARY || expr->kind == AST_VALUE); - if (expr->kind == AST_IDENT) { - Ast_Atom *atom = (Ast_Atom *)expr; - if (atom->intern_val == intern) { - return true; - } - } - return false; -} - -CORE_Static Operand -resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_and_const_string_context, Ast_Scope *field_access_scope) { - if (!ast && is_flag_set(flags, AST_CAN_BE_NULL)) return {}; - assert(is_flag_set(ast->flags, AST_EXPR)); - assert(ast->parent_scope->kind == AST_SCOPE || ast->parent_scope->kind == AST_FILE); - if (ast->flags & AST_COMPILER_BREAKPOINT) Breakpoint; - - switch (ast->kind) { - - CASE(IDENT, Atom) { - Ast_Scope *scope = field_access_scope ? field_access_scope : node->parent_scope; - Search_Flag flag = field_access_scope ? SEARCH_ONLY_CURRENT_SCOPE : 0; - - // @consider: Maybe instead of using AST_IDENT another case should be - // added like AST_RESOLVED or something because currently the identifier - // can already be resolved and stuff like that - - // if (node->intern_val == pctx->intern("MAP"_s)) { - // __debugbreak(); - // } - - // When copying polymorphs we fill out resolved_decl in - // identifiers, so it can happen that we have already resolved the name - Ast_Decl *decl = node->resolved_decl; - if (!decl) decl = resolve_name(scope, node->pos, node->intern_val, flag | RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED); - - // Substitute lambda alias - if (decl->kind == AST_CONST && decl->resolved_decl && decl->resolved_decl->kind == AST_LAMBDA) { - decl = decl->resolved_decl; - } - - node->resolved_decl = decl; - node->resolved_type = node->resolved_decl->type; - - Operand result = operand(node->resolved_decl); - if (result.is_const && decl->kind != AST_NAMESPACE) { - rewrite_into_const(node, Ast_Atom, node->resolved_decl->value); - node->resolved_decl = decl; - } - - return result; - BREAK(); - } - - CASE(VALUE, Atom) { - return operand_const_rvalue(node->value); - BREAK(); - } - - // Typespec array [32]int - CASE(ARRAY, Array) { - Operand type = resolve_expr(node->base, AST_CANT_BE_NULL, 0, 0); - Operand expr = require_const_int(node->expr, AST_CAN_BE_NULL); - if (type.type != pctx->type_type) compiler_error(node->pos, "Prefix array operator is only allowed on types"); - - if (node->expr) { - type_complete(type.type_val); - type.type_val = type_array(type.type_val, (S32)bigint_as_unsigned(&expr.big_int_val)); - } - else { - type.type_val = type_slice(type.type_val, node); - } - - // If this is a type we want to rewrite that type - // into an integer constant which should be an id - // of that type - if (!is_flag_set(flags, RESOLVE_TYPESPEC)) { - rewrite_into_const(node, Ast_Array, type.value); - } - - node->resolved_type = type.type_val; - return operand_type(node->resolved_type); - BREAK(); - } - - CASE(LAMBDA_EXPR, Lambda) { - node->resolved_type = resolve_lambda_type(node); - Operand result = operand_type(node->resolved_type); - try_resolving_lambda_scope(&result, node, node->resolved_type); - return result; - BREAK(); - } - - CASE(INDEX, Index) { - Operand left = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, field_access_scope); - Operand index = resolve_expr(node->index, AST_CANT_BE_NULL, 0, 0); - if (!(is_int(index.type) || is_enum(index.type))) - compiler_error(node->pos, "Trying to index the array with invalid type, expected [Int] got instead %Q", typestring(index.type)); - - node->index_original_type = left.type; - node->resolved_type = left.type->arr.base; - if (left.type == pctx->type_pointer_to_char) { - node->resolved_type = pctx->type_char; - } - if (left.type == pctx->type_pointer_to_void) { - compiler_error(node->pos, "Trying to index a void pointer is invalid"); - } - else if (is_string(left.type)) { - - // @warning: not sure about this context thing here. - // I didn't test it much so might need to delete later. - if (is_untyped(left.type)) { - if (compound_and_const_string_context) { - if (!is_string(compound_and_const_string_context)) { - compiler_error(node->pos, "Type mismatch, it's %Q but expected %Q", typestring(left.type), typestring(compound_and_const_string_context)); - } - left.type = compound_and_const_string_context; - } - else { - left.type = pctx->type_string; - } - } - node->resolved_type = pctx->type_u8; - } - - try_propagating_resolved_type_to_untyped_literals(node->index, pctx->type_int); - try_propagating_resolved_type_to_untyped_literals(node->expr, left.type); - - if (!left.type) - compiler_error(node->expr->pos, "Internal compiler error: type of array is null somehow"); - if (is_untyped(left.type)) - compiler_error(node->expr->pos, "Internal compiler error: type of array is marked as untyped somehow"); - - if (is_string(left.type)) { - return operand_lvalue_set_flag_on_node(pctx->type_u8, node); - } - - if (!is_array(left.type) && !is_pointer(left.type) && !is_slice(left.type)) { - compiler_error(node->pos, "Indexing variable that is not an [Array] or [Pointer], it's of type %Q instead", typestring(left.type)); - } - - return operand_lvalue_set_flag_on_node(node->resolved_type, node); - BREAK(); - } - - CASE(BINARY, Binary) { - if (token_is_assign(node->op)) { - assert(is_flag_set(node->flags, AST_STMT)); - Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); - if (!left.is_lvalue) compiler_error(node->pos, "Assigning to rvalue"); - Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, left.type, field_access_scope); - - // @warning: Could be buggy, previously we strictly matched if types are exact - // need to test this with slices and stuff - make_sure_value_is_compatible_with_type(node->pos, &right, left.type, TYPE_AND_EXPR_REQUIRED); - - node->resolved_type = right.type; - try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type, node->right->resolved_type); - try_propagating_resolved_type_to_untyped_literals(node->right, node->resolved_type, node->left->resolved_type); - return {}; - } - else if (node->op == TK_Arrow) { - return resolve_cast(node); - } - else if (node->op == TK_Dot) { - Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); - Ast_Scope *scope = 0; - - Ast_Decl *decl = left.resolved_decl; - if (decl && decl->kind == AST_NAMESPACE) { - scope = decl->scope; - } - - else { - // Make sure it's a type but also not a type id - Ast_Type *type = left.type; - if (type == pctx->type_type && left.type_val) { - if (is_enum(left.type_val) || is_struct(left.type_val) || is_union(left.type_val)) { - type = left.type_val; - } - } - - // We need 2 resolved types, for left and right - // Then we can compare if we got a pointer we need to - // figure that out to replace '.' with '->' for pointer structs - node->dot_access_step_resolution = type; - - if (is_pointer(type)) type = type->base; - type_complete(type); - - bool is_dottable_type = type->kind == TYPE_ENUM || type->kind == TYPE_STRUCT || type->kind == TYPE_UNION; - if (is_dottable_type == false) compiler_error(node->pos, "Type %Q doesn't have anything to access using '.' operator", typestring(type)); - - scope = ((Ast_Decl *)type->ast)->scope; - if (!scope) { - compiler_error(node->pos, "Internal compiler error? Type %Q doesn't have scope, you cannot use '.' on this variable", typestring(type)); - } - } - - Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, scope); - node->resolved_type = right.type; - - if (right.is_const) { - rewrite_into_const(node, Ast_Binary, right.value); - } - return right; - } - else { - Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); - Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, field_access_scope); - B32 is_const = left.is_const && right.is_const; - B32 proceed_to_default_operator_handler = true; - - // Try finding a operator overload - if (!is_const) { - Value left_copy = left.value; - Value right_copy = right.value; - try_converting_untyped_to_default_type(&left_copy); - try_converting_untyped_to_default_type(&right_copy); - U64 hash = calculate_hash_for_arguments(left_copy.type, right_copy.type); - Ast_Decl *operator_overload = resolve_operator_overload(node->parent_scope, left_copy.type, right_copy.type, node->pos, node->op, hash); - - if (operator_overload) { - proceed_to_default_operator_handler = false; - left.value = left_copy; - right.value = right_copy; - // @warning: might be buggy, added after a long break - // Propagate const - if (left.is_const) { - Ast_Atom *atom_left = (Ast_Atom *)node->left; - atom_left->value = left.value; - } - if (right.is_const) { - Ast_Atom *atom_right = (Ast_Atom *)node->right; - atom_right->value = right.value; - } - - node->resolved_type = operator_overload->type->func.ret; - node->resolved_operator_overload = operator_overload; - - // We opt out early because we convert all literals to default type - // We don't need to propagate resolved type - return operand_rvalue(node->resolved_type); - } - } - - // Not found or constant then go for default option - Value value = {}; - if (proceed_to_default_operator_handler) { - value = eval_binary(node->pos, node->op, left.value, right.value, is_const); - node->resolved_type = value.type; - } - - if (is_const) { - // We don't need to propagte types for const values cause we are rewritting them - rewrite_into_const(node, Ast_Binary, value); - return operand_const_rvalue(value); - } - else { - try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type); - try_propagating_resolved_type_to_untyped_literals(node->right, node->resolved_type); - return operand_rvalue(node->resolved_type); - } - } - BREAK(); - } - - CASE(VAR, Decl) { - resolve_stmt(node, 0); - return {}; - BREAK(); - } - - CASE(UNARY, Unary) { - Operand value = resolve_expr(node->expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - if (node->op == TK_Pointer) { - if (value.type->kind == TYPE_POINTER) { - node->resolved_type = value.type->base; - return operand_lvalue_set_flag_on_node(node->resolved_type, node); - } - else if (value.type->kind == TYPE_TYPE) { - value.type_val = type_pointer(value.type_val); - if (!is_flag_set(flags, RESOLVE_TYPESPEC)) { - rewrite_into_const(node, Ast_Unary, value.value); - } - - node->resolved_type = value.type_val; - return operand_type(node->resolved_type); - } - else { - compiler_error(node->pos, "Dereferencing expression %Q that is not a [Pointer] or [Type]", typestring(value.type)); - return {}; - } - } - else if (node->op == TK_Dereference) { - if (!value.is_lvalue) { - compiler_error(node->pos, "Can't take a pointer of this expression. It's not bound to anything so taking a pointer of it would be pretty silly."); - } - - node->resolved_type = type_pointer(value.type); - return operand_lvalue_set_flag_on_node(node->resolved_type, node); - } - else { - - // Try finding a operator overload - B32 proceed_to_default_operator_handler = true; - if (!value.is_const) { - U64 hash = calculate_hash_for_arguments(value.type); - Ast_Decl *operator_overload = resolve_operator_overload(node->parent_scope, value.type, 0, node->pos, node->op, hash); - if (operator_overload) { - proceed_to_default_operator_handler = false; - node->resolved_type = operator_overload->type->func.ret; - node->resolved_operator_overload = operator_overload; - } - } - - if (proceed_to_default_operator_handler) { - eval_unary(node->pos, node->op, &value); - node->resolved_type = value.type; - } - - if (value.is_const) { - rewrite_into_const(node, Ast_Unary, value.value); - return operand_const_rvalue(value.value); - } - - return operand_rvalue(node->resolved_type); - } - - BREAK(); - } - - CASE(COMPOUND, Call) { - Operand op = resolve_expr(node->typespec, inherit_flag(flags, AST_CAN_BE_NULL), 0, field_access_scope); - - Ast_Type *type = op.type; - if (type) { - if (type != pctx->type_type) - compiler_error(node->pos, "Expected type [Type] got instead %Q", typestring(type)); - type = op.type_val; - } - if (!type) - type = compound_and_const_string_context; - if (type == pctx->type_void || type == nullptr) - compiler_error(node->pos, "Couldn't infer type of compound expression"); - - type_complete(type); - node->resolved_type = type; - if (is_array(type) || is_slice(type)) { - resolve_compound_array(node, type); - } - else if (is_struct(type) || is_union(type)) { - resolve_compound_struct(node, type); - } - else { - compiler_error(node->pos, "Internal compiler error: Invalid type was passed to the compound expression, should have been an array, struct or slice"); - } - - return operand_lvalue_set_flag_on_node(type, node); - - BREAK(); - } - - CASE(CALL, Call) { - if (expr_atom_is_equal_intern(node->name, pctx->intern_sizeof)) { - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - - Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - node->kind = AST_SIZE_OF; - - Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; - type_complete(type); - - if (type->size == 0) { - compiler_error(node->pos, "Internal compiler error: calling sizeof but the resulting size of type is obviously invalid suggesting that type was not completed properly"); - } - - Value v = value_int(type->size); - rewrite_into_const(node, Ast_Builtin, v); - return operand_const_rvalue(v); - } - - else if (expr_atom_is_equal_intern(node->name, pctx->intern_typeof)) { - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - - Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - node->kind = AST_TYPE_OF; - Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; - - Operand result = operand_type(type); - rewrite_into_const(node, Ast_Builtin, result.value); - return result; - } - - else if (expr_atom_is_equal_intern(node->name, pctx->intern_alignof)) { - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - node->kind = AST_ALIGN_OF; - - Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; - type_complete(type); - if (type->size == 0) { - compiler_error(node->pos, "Internal compiler error: calling sizeof but the resulting size of type is obviously invalid suggesting that type was not completed properly"); - } - - Value v = value_int(type->align); - rewrite_into_const(node, Ast_Builtin, v); - return operand_const_rvalue(v); - } - - else if (expr_atom_is_equal_intern(node->name, pctx->intern_len)) { - Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); - - Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - node->kind = AST_LENGTH_OF; - node->resolved_type = pctx->type_int; - if (is_array(name.type)) { - Value value = value_int(name.type->arr.size); - rewrite_into_const(node, Ast_Builtin, value); - return operand_const_rvalue(value); - } - else if (name.type->kind == TYPE_UNTYPED_STRING) { - Value value = value_int(name.intern_val.len); - rewrite_into_const(node, Ast_Builtin, value); - return operand_const_rvalue(value); - } - else if (is_array(name.type) || is_slice(name.type) || is_string(name.type)) { - return operand_rvalue(pctx->type_int); - } - else compiler_error(node->pos, "Can't get length of type %Q", typestring(name.type)); - } - - else { - Operand name = resolve_expr(node->name, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); - - if (name.resolved_decl->flags & AST_POLYMORPH) { - assert(name.resolved_decl->flags & AST_PARENT_POLYMORPH); - Ast_Decl *poly = name.resolved_decl; - - if (poly->kind == AST_STRUCT || poly->kind == AST_UNION) { - Ast_Decl *instance = get_or_instantiate_polymorph_type(node->pos, poly, node->exprs, field_access_scope); - return operand_type(instance->type_val); - } - } - - /* - We need an algorithm and other concretes for correct matching of arrays, var args and others. - - (a: int = 5, b: int) Disallowed, this is not lua where table has indexed and keyed values at the same time - (a: int = 5, b: int = 10) - (a = 2, b = 10) - (32, 4) - (32, b = 32) - (32, a = 32) Error - (b = 3, b = 4) Error - - (a: int, b: ..String) - (a: int, b: ..Any) - (10, 10, 10) - (10, b = {10, 10, 10, 10}) - (10, ..slice) // We want to avoid accidental slice pass - (10, b = ..slice) - (a = 10, 10, 10) error, there shouldn't be normal args after named - - - (a: int, b: ..#vargs) // Var args will banish all forms of named arguments - (a = 10, b = 10) error - (b = 10, 10) error - (10, 10) OK - Any form of named arguments is invalid - - - (a: int, b: []Any) - (a = 10, b = {1, 2, "asd"}) - (a: int, b: []String) - - */ - - // - // Regular call - // - if (!name.resolved_decl) { - compiler_error(node->pos, "Internal compiler error: Failed to propagate a resolved lambda declaration from atom resolution"); - } - if (name.resolved_decl->kind != AST_LAMBDA) { - compiler_error(node->pos, "Calling %Q which is not a [Lambda]", typestring(name.type)); - } - node->resolved_decl = name.resolved_decl; - - /* - @warning - We only have one instance of a given Lambda type for example (a: Vec3): Vec3. - Even though we might have multiple functions with this signature. - This creates a problem cause sometimes we might refer to the wrong Ast. - Need to be careful!!! - */ - - For(node->exprs) { - if (is_flag_set(it->call_flags, CALL_INDEX)) { - compiler_error(it->pos, "Index inside a call is not allowed!"); - } - } - - Scoped_Arena scratch(pctx->scratch); - Ast_Lambda *lambda = name.resolved_decl->lambda; - struct Match { - Ast_Decl *lambda_arg; - Ast_Call_Item *call_arg; - }; - Array matches = {scratch.arena}; - S64 default_iter = 0; - - // For every defined argument in the lambda we seek for - // a corresponding item in the call expression. - // - // This algorithm is cool because it results in correct argument order. - // It's kind of complicated, especially the default_iter stuff is kind - // of weird but it works very well. The default_iter is only incremented - // on successful match because we are iterating through entire call expression - // every time. - // Might be wise to revise it later but the approach of starting with - // lambda_args seems correct. - For2(lambda->args, lambda_arg) { - // Variadic functions cannot have default arguments - // we make sure of that when resolving the lambda - if (lambda_arg->kind == AST_VARGS_LAMBDA_PARAM) { - For(node->exprs) { - if (is_flag_set(it->call_flags, CALL_NAME)) { - compiler_error(it->pos, "Variadic function doesnt allow for named arguments!"); - } - - S64 idx = node->exprs.get_index(it); - if (idx >= default_iter) { - matches.add({lambda_arg, it}); - } - } - break; - } - - Ast_Call_Item *item = 0; - For(node->exprs) { - S64 it_index = node->exprs.get_index(it); - - bool named_argument = is_flag_set(it->call_flags, CALL_NAME); - bool named_argument_already_appeared = it_index > default_iter; - if (named_argument) { - Ast_Atom *name = it->name; - assert(name->kind == AST_IDENT); - if (name->intern_val.str == lambda_arg->name.str) item = it; - } - - else if (it_index == default_iter) { - default_iter += 1; - item = it; - } - - else if (named_argument_already_appeared) compiler_error(it->pos, "Positional argument after named argument is not permitted"); - - if (item) break; - } - - // If we couldn't find corresponding call item we should seek - // for a default value inside the lambda declaration - if (!item) { - if (!lambda_arg->expr) compiler_error(node->pos, node->resolved_decl->pos, "Required value: %Q in lambda call was not passed", lambda_arg->name); - item = ast_call_item(lambda_arg->expr->pos, 0, 0, lambda_arg->expr); - item->resolved_type = lambda_arg->expr->resolved_type; - } - matches.add({lambda_arg, item}); - } - - // Make sure we found every item. - For2(node->exprs, call_it) { - bool included = false; - For2(matches, match_it) { - if (call_it == match_it.call_arg) { - included = true; - break; - } - } - if (!included) compiler_error(call_it->pos, "Unknown argument to a function call, couldn't match it with any of the declared arguments"); - } - - // Find the polymorph - if (lambda->flags & AST_POLYMORPH) { - Array replacements = {scratch.arena}; - ForArrayRemovable(matches) { - ForArrayRemovablePrepare(matches); - if (it.lambda_arg->flags & AST_POLYMORPH) replacements.add(it.call_arg); - if (it.lambda_arg->flags & AST_IDENT_POLYMORPH) ForArrayRemovableDeclare(); - } - - Ast_Decl *poly = name.resolved_decl; - Ast_Decl *instance = get_or_instantiate_polymorph_lambda(node->pos, poly, replacements, field_access_scope); - node->resolved_decl = instance; - lambda = instance->lambda; - - // We now need to fix the matches list CAUSE ITS FUCKED - // Thanks polymorphism - For2(matches, match_it) { - For2(lambda->args, lambda_it) { - if (match_it.lambda_arg->name == lambda_it->name) { - match_it.lambda_arg = lambda_it; - break; - } - } - } - } - - // Typecheck the arguments and produce properly ordered list of arguments for codegeneration - Array items = {scratch.arena}; - For(matches) { - Ast_Call_Item *item = it.call_arg; - Ast_Decl *lambda_arg = it.lambda_arg; - if (lambda_arg->flags & AST_IDENT_POLYMORPH) continue; - if (lambda_arg->kind == AST_VARGS_LAMBDA_PARAM) { - Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type, field_access_scope); - try_converting_untyped_to_default_type(&expr); - if (expr.type == pctx->type_string) expr.type = pctx->type_pointer_to_char; - item->resolved_type = expr.type; - } - else { - Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type, field_access_scope); - make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type, TYPE_AND_EXPR_REQUIRED); - item->resolved_type = lambda_arg->type; - } - try_propagating_resolved_type_to_untyped_literals(item->item, item->resolved_type); - items.add(item); - } - node->exprs = items.tight_copy(pctx->perm); - - node->resolved_type = lambda->resolved_type->func.ret; - return operand_rvalue(node->resolved_type); - // - // CALL End - // - } - BREAK(); - } - - invalid_default_case; - } - - invalid_return; -} - -CORE_Static Ast_Type * -get_type_base(Ast_Type *type) { - switch (type->kind) { - case TYPE_POINTER: - case TYPE_SLICE: - case TYPE_ARRAY: return get_type_base(type->base); - default: return type; - } -} - -CORE_Static void -resolve_name_for_global_decl(Ast_Decl *node) { - if (node->kind == AST_LAMBDA && is_flag_set(node->expr->flags, AST_FOREIGN)) return; // @cleanup ? - if (is_flag_set(node->flags, AST_FOREIGN)) return; - if (!is_flag_set(node->flags, AST_GLOBAL)) return; - - Ast_Scope *s = node->parent_scope; - Intern_String namespace_name = {}; - for (; s; s = s->parent_scope) { - if (s->first_namespace_name.len) { - namespace_name = s->first_namespace_name; - break; - } - } - if (namespace_name.len) { - node->unique_name = pctx->internf("%Q_%Q", namespace_name, node->unique_name); - } - // printf("%.*s\n", (int)node->unique_name.len, node->unique_name.str); -} - -CORE_Static void -resolve_decl(Ast_Decl *ast) { - if (ast->state == DECL_RESOLVED) - return; - else if (ast->state == DECL_RESOLVING) { - compiler_error(ast->pos, "Cyclic dependency of %s", ast->name.str); - return; - } - if (ast->flags & AST_PARENT_POLYMORPH) return; - assert(ast->state == DECL_NOT_RESOLVED); - - ast->state = DECL_RESOLVING; - Ast_Decl *node = (Ast_Decl *)ast; - node->unique_name = node->name; - if (ast->flags & AST_COMPILER_BREAKPOINT) Breakpoint; - { - switch (ast->kind) { - case AST_LAMBDA: { - Ast_Lambda *lambda = node->lambda; - lambda->resolved_type = resolve_lambda_type(lambda); - Operand result = operand_type(lambda->resolved_type); - - // @note: top level lambda needs to get marked as resolved - // so that the cyclic dependency wont trigger - node->type = lambda->resolved_type; - node->state = DECL_RESOLVED; - - // @todo: We also need to make sure there is a return value when ret type is not void - try_resolving_lambda_scope(&result, lambda, node->type); - node->value = result.value; - - Scoped_Arena scratch(pctx->scratch); - - if (is_flag_set(node->flags, AST_OPERATOR_OVERLOAD)) { - node->unique_name = pctx->internf("OPERATOR_%Q%d", node->overload_op_info->name, pctx->global_decl_ids++); - } - else { - if (node->flags & AST_POLYMORPH_INSTANCE) { - node->unique_name = pctx->internf("%Q%d", node->name, pctx->global_decl_ids++); - } - resolve_name_for_global_decl(node); - } - } break; - - case AST_CONST: { - // @warning: if in the future we add type here then pass it to resolve expr for - // compound - Operand op = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, 0); - if (!op.is_const) { - compiler_error(node->pos, "Assigning a value that is not constant to a constant declaration"); - } - - node->value = op.value; - if (op.value.type == pctx->type_type) { - node->kind = AST_TYPE; - if (is_flag_set(node->flags, AST_STRICT)) { - node->type_val = type_copy(pctx->perm, node->type_val); - } - } - resolve_name_for_global_decl(node); - - break; - } - - case AST_VAR: { - Ast_Type *type = node->type; - if (!type) type = resolve_typespec(node->typespec, AST_CAN_BE_NULL | RESOLVE_TYPESPEC_COMPLETE); - Operand op = resolve_expr(node->expr, AST_CAN_BE_NULL, type, 0); - assert(op.type != 0 || type != 0); - - make_sure_value_is_compatible_with_type(node->pos, &op, type, EXPR_CAN_BE_NULL | TYPE_CAN_BE_NULL); - node->value = op.value; - - if (is_flag_set(node->flags, AST_FOREIGN)) { - if (node->expr) compiler_error(node->pos, "#foreign variable shouldn't have value"); - } - - try_propagating_resolved_type_to_untyped_literals(node->expr, node->type); - - if (op.is_const) { - set_flag(node->flags, AST_VAR_IS_CONST); - } - // if (node->name == pctx->internf("CurrentMap")) __debugbreak(); - resolve_name_for_global_decl(node); - break; - } - - case AST_NAMESPACE: - break; - - case AST_ENUM: { - Ast_Type *type_of_enum = resolve_typespec(node->typespec, AST_CAN_BE_NULL); - - node->type = pctx->type_type; - node->type_val = type_enum(node, type_of_enum); - - S64 value = 1; - For2(node->scope->decls, decl) { - Operand op = {}; - if (decl->expr) { - op = require_const_int(decl->expr, AST_CANT_BE_NULL); - value = bigint_as_signed(&op.big_int_val) + 1; - } - else { - decl->state = DECL_RESOLVED; - op.type = node->type_val; - bigint_init_signed(&op.big_int_val, value); - if (is_flag_set(node->flags, AST_FLAG)) { - value = value << 1; - } - else { - value += 1; - } - } - - decl->value = op.value; - } - resolve_name_for_global_decl(node); - } break; - - invalid_default_case; - } - } - ast->state = DECL_RESOLVED; - - bool is_polymorph = is_flag_set(ast->flags, AST_POLYMORPH); - bool is_global = is_flag_set(ast->flags, AST_GLOBAL); - if (is_global && !is_polymorph) { - add(pctx->perm, &pctx->ordered_decls, ast); - } -} + +CORE_Static Resolve_Flag +inherit_flag(Resolve_Flag flag, B32 ast_can_be_null) { + unset_flag(flag, AST_CAN_BE_NULL); + set_flag(flag, ast_can_be_null); + return flag; +} + +//----------------------------------------------------------------------------- +// Operands +//----------------------------------------------------------------------------- +CORE_Static Operand +operand(Ast_Decl *decl) { + Operand result = {}; + result.type = decl->type; + result.is_const = decl->kind != AST_VAR ? true : false; + result.is_lvalue = decl->kind == AST_CONST ? false : true; // Cant assign to const values + result.value = decl->value; + result.resolved_decl = decl; + return result; +} + +static Operand +operand_type(Ast_Type *type) { + Operand result = {}; + result.type = pctx->type_type; + result.is_const = true; + result.is_lvalue = false; + result.type_val = type; + return result; +} + +CORE_Static Operand +operand_int(BigInt big_int) { + Operand result = {}; + result.type = pctx->untyped_int; + result.big_int_val = bigint_copy(pctx->perm, &big_int); + result.is_const = true; + result.is_lvalue = false; + return result; +} + +CORE_Static Operand +operand_str(Intern_String intern_val) { + Operand result = {}; + result.type = pctx->type_string; + result.intern_val = intern_val; + result.is_const = true; + result.is_lvalue = false; + return result; +} + +CORE_Static Operand +operand_lambda(Ast_Type *type) { + Operand result = {}; + result.type = type; + result.is_const = true; + result.is_lvalue = false; + return result; +} + +CORE_Static Operand +operand_const_rvalue(Value value) { + Operand result = {}; + result.is_const = true; + result.value = value; + return result; +} + +CORE_Static Operand +operand_lvalue_set_flag_on_node(Ast_Type *type, Ast *ast) { + Operand result = {}; + result.type = type; + result.is_const = false; + result.is_lvalue = true; + set_flag(ast->flags, AST_IS_LVALUE); + return result; +} + +CORE_Static Operand +operand_rvalue(Ast_Type *type) { + Operand result = {}; + result.type = type; + result.is_const = false; + result.is_lvalue = false; + return result; +} + +CORE_Static Ast_Type * +get_default_type_from_untyped(Ast_Type *type) { + switch (type->kind) { + case TYPE_UNTYPED_INT: return pctx->type_int; break; + case TYPE_UNTYPED_BOOL: return pctx->type_bool; break; + case TYPE_UNTYPED_STRING: return pctx->type_string; break; + case TYPE_UNTYPED_FLOAT: return pctx->type_f32; break; + default: invalid_codepath; + } + return 0; +} + +CORE_Static void +try_converting_untyped_to_default_type(Value *op) { + if (is_untyped(op->type)) { + op->type = get_default_type_from_untyped(op->type); + } +} + +CORE_Static void +try_converting_untyped_to_default_type(Operand *op) { + try_converting_untyped_to_default_type(&op->value); +} + +CORE_Static void +check_value_bounds(Token *pos, Value *a) { + if (!is_int(a->type)) return; + + Scoped_Arena scratch(pctx->scratch); + if (!bigint_fits_in_bits(&a->big_int_val, a->type->size * 8, is_signed_int(a->type))) { + const char *string = bigint_to_error_string(scratch.arena, &a->big_int_val, 10); + compiler_error(pos, "Value %s doesn't fit in type %Q", string, typestring(a->type)); + } +} + +CORE_Static void +convert_untyped_to_typed(Token *pos, Value *a, Ast_Type *new_type) { + assert(new_type); + if (a->type == 0) return; + if (is_typed(a->type)) return; + + if (is_any(new_type)) + new_type = get_default_type_from_untyped(a->type); + else if (is_int(a->type) && is_int(new_type)) + assert(a->type == pctx->untyped_int); + else if (is_int(a->type) && is_enum(new_type)) + ; + else if (is_int(a->type) && is_float(new_type)) + a->f64_val = bigint_as_float(&a->big_int_val); // @leak bigint + else if (is_int(a->type) && is_pointer(new_type)) + ; + else if (is_float(a->type) && is_float(new_type)) + ; // nothing to do + else if (is_bool(a->type) && is_bool(new_type)) + ; // nothing to do + else if (is_string(a->type) && is_string(new_type)) + ; // nothing to do + else compiler_error(pos, "Type mismatch when converting from %Q to %Q", typestring(a->type), typestring(new_type)); + + a->type = new_type; + check_value_bounds(pos, a); +} + +CORE_Static Ast_Type * +convert_untyped_to_typed(Token *pos, Ast_Type *type, Ast_Type *new_type) { + Value value = {}; + value.type = type; + convert_untyped_to_typed(pos, &value, new_type); + return value.type; +} + +CORE_Static void +make_sure_types_are_compatible_for_constant_evaluation(Token *pos, Value *a, Value *b) { + if ((is_pointer(a->type) && is_int(b->type)) || (is_pointer(b->type) && is_int(a->type))) { + return; + } + else if (is_pointer(a->type) && is_pointer(b->type)) { + return; + } + else if (is_typed(a->type) && is_typed(b->type)) { + if (a->type != b->type) { + compiler_error(pos, "Type mismatch in make_sure_types_are_compatible_for_constant_evaluation - left: %Q right: %Q", typestring(a->type), typestring(b->type)); + } + } + + if (is_untyped(a->type) && is_typed(b->type)) { + assert(is_typed(b->type)); + convert_untyped_to_typed(pos, a, b->type); + } + else if (is_typed(a->type) && is_untyped(b->type)) { + assert(is_typed(a->type)); + convert_untyped_to_typed(pos, b, a->type); + } + else if (is_int(a->type) && is_float(b->type)) { + *a = value_float(a->big_int_val); + } + else if (is_float(a->type) && is_int(b->type)) { + *b = value_float(b->big_int_val); + } +} + +CORE_Static Value +compare_values(Token *pos, Token_Kind op, Value a, Value b, bool is_const) { + if (is_enum(a.type) && (a.type == b.type)) + goto skip_eval; + if (a.type->kind == TYPE_TYPE && b.type->kind == TYPE_TYPE) + goto skip_eval; + if (!(is_numeric(a.type) && is_numeric(b.type))) + compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); + + make_sure_types_are_compatible_for_constant_evaluation(pos, &a, &b); + +skip_eval: + B32 result = 0; + if (is_const) { + switch (a.type->kind) { + case TYPE_TYPE: { + switch (op) { + case TK_Equals: result = a.type_val->type_id == b.type_val->type_id; break; + case TK_NotEquals: result = a.type_val->type_id != b.type_val->type_id; break; + default: goto failure; + } + } break; + CASE_INT : { + CmpRes cmp = bigint_cmp(&a.big_int_val, &b.big_int_val); + switch (op) { + case TK_LesserThenOrEqual: result = (cmp == CMP_LT) || (cmp == CMP_EQ); break; + case TK_GreaterThenOrEqual: result = (cmp == CMP_GT) || (cmp == CMP_EQ); break; + case TK_GreaterThen: result = cmp == CMP_GT; break; + case TK_LesserThen: result = cmp == CMP_LT; break; + case TK_Equals: result = cmp == CMP_EQ; break; + case TK_NotEquals: result = cmp != CMP_EQ; break; + default: goto failure; + } + } break; + CASE_BOOL : { + switch (op) { + case TK_Equals: result = a.bool_val == b.bool_val; break; + case TK_NotEquals: result = a.bool_val != b.bool_val; break; + default: goto failure; + } + } break; + CASE_FLOAT : { + switch (op) { + case TK_LesserThenOrEqual: result = a.f64_val <= b.f64_val; break; + case TK_GreaterThenOrEqual: result = a.f64_val >= b.f64_val; break; + case TK_GreaterThen: result = a.f64_val > b.f64_val; break; + case TK_LesserThen: result = a.f64_val < b.f64_val; break; + case TK_Equals: result = a.f64_val == b.f64_val; break; + case TK_NotEquals: result = a.f64_val != b.f64_val; break; + default: goto failure; + } + } break; + + CASE_STRING : { + goto failure; + } break; + + default: { + failure: + compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); + } + } + } + + return value_bool(result); +} + +CORE_Static Value +eval_binary(Token *pos, Token_Kind op, Value a, Value b, bool is_const) { + if (token_is_compare(op)) + return compare_values(pos, op, a, b, is_const); + + if (!(is_numeric(a.type) && is_numeric(b.type))) + compiler_error(pos, "Constant application of binary %s on values of type %Q and %Q is not allowed", name(op), typestring(a.type), typestring(b.type)); + + make_sure_types_are_compatible_for_constant_evaluation(pos, &a, &b); + + // @todo: Add compiler error to the invalid switch cases !!! + Value result = {}; + result.type = a.type; + if (is_const) { + switch (a.type->kind) { + CASE_INT : { + switch (op) { + case TK_BitXor: bigint_xor(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_BitAnd: bigint_and(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_BitOr: bigint_or(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Add: bigint_add(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Sub: bigint_sub(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Mul: bigint_mul(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Div: bigint_div_trunc(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_Mod: bigint_mod(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_LeftShift: bigint_shl(&result.big_int_val, &a.big_int_val, &b.big_int_val); break; + case TK_RightShift: + bigint_shr(&result.big_int_val, &a.big_int_val, &b.big_int_val); + break; + invalid_default_case; + } + } break; + CASE_BOOL : { + switch (op) { + case TK_And: result.bool_val = a.bool_val && b.bool_val; break; + case TK_Or: + result.bool_val = a.bool_val || b.bool_val; + break; + invalid_default_case; + } + } break; + CASE_FLOAT : { + switch (op) { + case TK_Add: result.f64_val = a.f64_val + b.f64_val; break; + case TK_Sub: result.f64_val = a.f64_val - b.f64_val; break; + case TK_Mul: result.f64_val = a.f64_val * b.f64_val; break; + case TK_Div: + result.f64_val = a.f64_val / b.f64_val; + break; + invalid_default_case; + } + } break; + CASE_STRING : { + invalid_codepath; + } break; + invalid_default_case; + } + } + + return result; +} + +CORE_Static S64 +digit_count(const Value *a) { + S64 digit_count = a->big_int_val.digit_count * 64; + if (is_typed(a->type)) { + digit_count = a->type->size * 8; + } + return digit_count; +} + +CORE_Static void +eval_unary(Token *pos, Token_Kind op, Operand *operand) { + Value *a = &operand->value; + Ast_Type *type = a->type; + if (!is_numeric(type)) + compiler_error(pos, "Unary [%s] cant be applied to value of type %Q", name(op), typestring(type)); + + if (op == TK_Not) + a->type = pctx->untyped_bool; + + if (op == TK_Increment || op == TK_Decrement || op == TK_PostIncrement || op == TK_PostDecrement) + if (!operand->is_lvalue) + compiler_error(pos, "Unary [%s] requires an assignable value(lvalue)", name(op)); + + if (!operand->is_const) + return; + + BigInt result = {}; + switch (op) { + case TK_Add: { + } break; + case TK_Sub: { + switch (type->kind) { + CASE_INT : { + bigint_negate(&result, &a->big_int_val); + a->big_int_val = result; + } break; + CASE_FLOAT : { + a->f64_val = -a->f64_val; + } break; + default: goto failure; + } + } break; + case TK_Neg: { + switch (type->kind) { + CASE_SINT: + case TYPE_UNTYPED_INT: + bigint_not(&result, &a->big_int_val, digit_count(a), 1); + break; + CASE_UINT: + bigint_not(&result, &a->big_int_val, digit_count(a), 0); + break; + default: goto failure; + } + a->big_int_val = result; + } break; + case TK_Not: { + switch (type->kind) { + CASE_INT : { + if (CMP_EQ == bigint_cmp_zero(&a->big_int_val)) + a->bool_val = 1; + a->bool_val = 0; + } break; + CASE_FLOAT: + a->bool_val = !a->f64_val; + break; + CASE_BOOL: + a->bool_val = !a->bool_val; + break; + default: goto failure; + } + } break; + default: + failure: + compiler_error(pos, "Constant application of unary %s on values of type %Q is not allowed", name(op), typestring(a->type)); + } +} + +CORE_Static void +make_sure_value_is_compatible_with_type(Token *pos, Operand *expr, Ast_Type *type, Typecheck_Flag debug_flag) { + if (type == expr->type) { + assert(type); + assert(expr->type); + return; + } + + if (!type || type->kind == TYPE_VARGS) { + assert(is_flag_set(debug_flag, TYPE_CAN_BE_NULL)); + assert(expr->type); + try_converting_untyped_to_default_type(expr); + } + else if (!expr->type) { + assert(is_flag_set(debug_flag, EXPR_CAN_BE_NULL)); + assert(type); + expr->type = type; + } + else if (is_any(type)) { + expr->type = type; + } + else if (is_void_pointer(type) && is_pointer(expr->type)) { + expr->type = type; + } + else if (is_pointer(type) && is_void_pointer(expr->type)) { + expr->type = type; + } + else if (is_untyped(expr->type)) { + convert_untyped_to_typed(pos, &expr->value, type); + } + else if (is_slice(type) && is_array(expr->type)) { + if (type->arr.slice_hash == expr->type->arr.slice_hash) + expr->type = type; + } + + if (type && expr->type != type) { + String expr_type_string = typestring(expr->type); + String type_string = typestring(type); + compiler_error(pos, "Assigning but incompatible types, expression: %Q expected var type: %Q", expr_type_string, type_string); + } + + type_complete(expr->type); + check_value_bounds(pos, &expr->value); + assert(expr->type); +} + +#define rewrite_into_const(ast, T, s) _rewrite_into_const(ast, sizeof(T), s) +CORE_Static void _rewrite_into_const(Ast *node, U64 ast_size, Value value) { + auto ast = (Ast_Atom *)node; + assert(ast_size >= sizeof(Ast_Atom)); + set_flag(ast->flags, AST_ATOM); + if (value.type && is_lambda(value.type)) { + ast->kind = AST_IDENT; + ast->value = value; + } + else { + ast->kind = AST_VALUE; + ast->value = value; + ast->resolved_type = value.type; + } +} + +CORE_Static void +inside_scope_search(Scope_Search *search, Ast_Scope *scope, int level) { + if (scope->visit_id == search->scope_visit_id) return; + scope->visit_id = search->scope_visit_id; + + // Search for declarations in current scope + For2(scope->decls, decl) { + if (decl->name == search->name) { + search->results.add(decl); + if (search->exit_on_find) { + return; + } + } + } + + // Search for declarations in imported implicitly scopes + // We dont want to descent into foreign implicit imports + // so we cap it with level and then we need to make sure that we + // descent into module 1 level away and it's files but no more then that + For(scope->implicit_imports) { + if (level > 0 && it->kind == AST_MODULE) continue; + inside_scope_search(search, it, level + 1); + + if (search->exit_on_find && search->results.len) { + return; + } + } +} + +CORE_Static void +scope_search(Scope_Search *search) { + // Climb the scope tree and search each scope in module and also + // search in implicitly imported scopes + For2(search->scopes, scope) { + for (Ast_Scope *it = scope; it; it = it->parent_scope) { + inside_scope_search(search, it, 0); + + if (search->exit_on_find && search->results.len) { + return; + } + + if (search->search_only_current_scope) { + return; + } + } + inside_scope_search(search, scope->module, 0); + } +} + +CORE_Static Scope_Search +make_scope_search(Allocator *arena, Ast_Scope *scope, Intern_String name) { + Scope_Search result = {}; + result.results.allocator = arena; + result.name = name; + result.scope_visit_id = ++pctx->scope_visit_id; + result.scopes = {arena}; + result.scopes.add(scope); + result.exit_on_find = false; + return result; +} + +CORE_Static Ast_Decl * +search_for_single_decl(Ast_Scope *scope, Intern_String name, bool error_if_no_matches = true) { + Scoped_Arena scratch(pctx->scratch); + + Scope_Search search = make_scope_search(scratch.arena, scope, name); + scope_search(&search); + + if (search.results.len == 0) { + if (error_if_no_matches) compiler_error(0, "Unidentified name [%s]", name.str); + return 0; + } + if (search.results.len > 1) { + compiler_error(search.results.data[0]->pos, search.results.data[1]->pos, "Found multiple definitions of name [%s]", name.str); + } + + return search.results.data[0]; +} + +CORE_Static Ast_Decl * +resolve_name(Ast_Scope *scope, Token *pos, Intern_String name, Search_Flag search_flags) { + Scoped_Arena scratch(pctx->scratch); + Scope_Search search = make_scope_search(scratch.arena, scope, name); + search.search_only_current_scope = search_flags & SEARCH_ONLY_CURRENT_SCOPE; + search.exit_on_find = true; + scope_search(&search); + + if (search.results.len == 0) { + compiler_error(pos, "Unidentified name [%s]", name.str); + } + + if (is_flag_set(search.results[0]->flags, AST_OPERATOR_OVERLOAD)) { + compiler_error(pos, "Internal compiler error: resolve_name got an overload"); + } + + Ast_Decl *decl = *search.results.data; + resolve_decl(decl); + return decl; +} + +CORE_Static Ast_Decl * +resolve_operator_overload(Ast_Scope *scope, Ast_Type *left, Ast_Type *right, Token *pos, Token_Kind op, U64 argument_hash) { + Ast_Operator_Info *op_info = get_operator_info(op); + if (op_info == 0) return 0; + + // Search for all possible candidates in three scopes + // The current module, left type definition module, right type definition module + Scoped_Arena scratch(pctx->scratch); + Scope_Search search = make_scope_search(scratch.arena, scope, op_info->op); + if (left->ast && left->ast->parent_scope) search.scopes.add(left->ast->parent_scope); + if (right && right->ast && right->ast->parent_scope) search.scopes.add(right->ast->parent_scope); + search.exit_on_find = false; + scope_search(&search); + + // Resolve them until we hit a match + Array matching_ops = {scratch.arena}; + For(search.results) { + resolve_decl(it); + if (it->type->func.hash_without_ret == argument_hash) { + matching_ops.add(it); + } + } + + if (matching_ops.len > 1) { + compiler_error(pos, "Found multiple matching operator overloads for [%Q]", op_info->op); + } + + if (matching_ops.len == 1) { + return matching_ops.data[0]; + } + + return 0; +} + +CORE_Static void +insert_into_scope(Ast_Scope *scope, Ast_Decl *decl) { + // This function is called when resolving statements + // inside code blocks, inserting lambda arguments into scope + // + // It's also called when scanning top level declarations of a module + // as such we probably don't want to call any resolve stuff here + Scoped_Arena scratch(pctx->scratch); + Scope_Search search = make_scope_search(scratch.arena, scope, decl->name); + search.search_only_current_scope = true; + scope_search(&search); + if (search.results.len != 0) { + if (!is_flag_set(decl->flags, AST_OPERATOR_OVERLOAD)) { + compiler_error(search.results.data[0]->pos, decl->pos, "[%s] is already defined", decl->name.str); + } + } + + add(pctx->perm, &scope->decls, decl); +} + +// +// Booleans are special in that untyped boolean appears from every comparison +// and children of that comparison don't have to be booleans so we have to +// propagate values of resolved children to each other +// +// int == untyped_int => bool +// need to propagate int to untyped_int +// +CORE_Static void +try_propagating_resolved_type_to_untyped_literals(Ast *ast, Ast_Type *type, Ast_Type *additional_not_bool_type) { + if (!type) compiler_error(ast->pos, "Internal compiler error: Type passed to try_propagating_resolved_type_to_untyped_literals is null"); + if (!ast) return; + if (is_untyped(type)) return; + + // @note: + // Normal untyped should only be 1 deep cause we make sure to colapse all constants + // and only constants can be untyped + if (!is_bool(type)) { + if (ast->kind != AST_VALUE) return; + + auto node = (Ast_Atom *)ast; + convert_untyped_to_typed(ast->pos, &node->value, type); + + // This only is required when we have arrays of Any type. + // That is because we are folding all the constants and there + // the resolved_type is set + node->resolved_type = node->value.type; + return; + } + + // Boolean special case + switch (ast->kind) { + CASE(IDENT, Atom) { + type = additional_not_bool_type ? additional_not_bool_type : type; + convert_untyped_to_typed(ast->pos, &node->value, type); + BREAK(); + } + + CASE(VALUE, Atom) { + type = additional_not_bool_type ? additional_not_bool_type : type; + convert_untyped_to_typed(ast->pos, &node->value, type); + BREAK(); + } + + CASE(BINARY, Binary) { + node->resolved_type = convert_untyped_to_typed(ast->pos, node->resolved_type, type); + try_propagating_resolved_type_to_untyped_literals(node->left, type, node->right->resolved_type); + try_propagating_resolved_type_to_untyped_literals(node->right, type, node->left->resolved_type); + BREAK(); + } + + CASE(CALL, Call) { + if (is_untyped(node->resolved_type)) + compiler_error(node->pos, "Internal compiler error: Resolved type of a call is marked as untyped"); + BREAK(); + } + + CASE(UNARY, Unary) { + node->resolved_type = convert_untyped_to_typed(ast->pos, node->resolved_type, type); + try_propagating_resolved_type_to_untyped_literals(node->expr, type); + BREAK(); + } + + default: { + } + } +} + +CORE_Static Ast_Type * +resolve_typespec(Ast_Expr *ast, Resolve_Flag flags) { + if (!ast && is_flag_set(flags, AST_CAN_BE_NULL)) + return 0; + + Operand resolved = resolve_expr(ast, flags | RESOLVE_TYPESPEC, 0, 0); + if (is_flag_set(flags, RESOLVE_TYPESPEC_COMPLETE)) + type_complete(resolved.type_val); + if (resolved.type != pctx->type_type) + compiler_error(ast->pos, "Expected [Type] got instead %Q", typestring(resolved.type)); + return resolved.type_val; +} + +CORE_Static Operand +resolve_and_require_bool(const char *error, Ast_Expr *expr, Resolve_Flag flags) { + if (!expr) { + if (flags == AST_CAN_BE_NULL) + return {}; + else compiler_error(0, "Compiler error: Null expression"); + } + + Operand op = resolve_expr(expr, flags, 0, 0); + if (!is_bool(op.type)) { + compiler_error(expr->pos, "Expected type [Bool] got instead type %Q :: %s", typestring(op.type), error); + } + + return op; +} + +CORE_Static Operand +require_const_int(Ast_Expr *expr, Resolve_Flag flags) { + Operand op = resolve_expr(expr, flags, 0, 0); + + if (expr == 0 && flags) + return op; + else if (expr == 0) + compiler_error(expr->pos, "This field cannot be null"); + + if (!op.is_const) + compiler_error(expr->pos, "Expected a const value"); + if (!(is_int(op.type) || is_enum(op.type))) + compiler_error(expr->pos, "Expected a constant integer got instead %Q", typestring(op.type)); + + return op; +} + +// ret is return value passed down the stack to check if type matches +CORE_Static void resolve_stmt(Ast *ast, Ast_Type *ret) { + if (!ast) return; + assert(ast->parent_scope->kind == AST_SCOPE); + if (ast->flags & AST_COMPILER_BREAKPOINT) Breakpoint; + + switch (ast->kind) { + CASE(RETURN, Return) { // @todo: need to check if all paths return a value + Ast_Type *value_type = pctx->type_void; + if (node->expr) { + Operand op = resolve_expr(node->expr, AST_CAN_BE_NULL, ret, 0); + convert_untyped_to_typed(node->pos, &op.value, ret); + value_type = op.type; + } + + if (value_type != ret) compiler_error(node->pos, "Return statement has different type then returned value, expecting: %Q got instead %Q", typestring(ret), typestring(value_type)); + node->resolved_type = ret; + + if (node->expr) try_propagating_resolved_type_to_untyped_literals(node->expr, ret); + + BREAK(); + } + + case AST_LAMBDA: + case AST_VAR: + CASE(CONST, Decl) { + resolve_decl(node); + insert_into_scope(node->parent_scope, node); + BREAK(); + } + + case AST_COMPILER_BREAKPOINT_STMT: { + __debugbreak(); + } break; + + case AST_LABEL: { + auto n = (Ast_Label *)ast; + insert_into_scope(n->parent_scope, n); + For(n->scope->stmts) { + resolve_stmt(it, ret); + } + } break; + + case AST_GOTO: { + auto n = (Ast_Goto *)ast; + auto decl = (Ast_Label *)search_for_single_decl(n->parent_scope, n->label); + if (!decl) compiler_error(n->pos, "Goto label is unidentified"); + if (decl->kind != AST_LABEL) compiler_error(n->pos, "Trying to goto identifier that is not a label"); + if (decl->enable_goto == false) compiler_error(decl->pos, "Label doesn't have goto enabled, add ':' at the end"); + } break; + + case AST_DEFER: { + auto n = (Ast_Defer *)ast; + For(n->scope->stmts) { + resolve_stmt(it, ret); + } + } break; + + case AST_CONTINUE: + case AST_BREAK: + case AST_PASS: { + + } break; + + CASE(RUNTIME_ASSERT, Builtin) { + resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); + try_propagating_resolved_type_to_untyped_literals(node->expr, pctx->type_bool); + BREAK(); + } + + CASE(CONSTANT_ASSERT, Builtin) { + Operand op = resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); + if (!op.is_const) compiler_error(node->pos, "#Assert expression required to be constant"); + if (op.bool_val == false) { + compiler_error(node->pos, "#Assert condition not met :: %.*s", (int)node->assert_message.len, node->assert_message.str); + } + BREAK(); + } + + CASE(FOR, For) { + if (node->init && node->cond == 0 && node->iter == 0) { + if (!is_flag_set(node->init->flags, AST_STMT)) { + node->cond = node->init; + node->init = 0; + } + } + + resolve_expr(node->init, AST_CAN_BE_NULL, 0, 0); + Operand op = resolve_expr(node->cond, AST_CAN_BE_NULL, 0, 0); + if (node->cond) { + if ((!node->init && !node->iter) && (is_array(op.type) || is_slice(op.type))) { + node->is_array_traversal = true; + if (is_slice(op.type)) node->is_also_slice_traversal = true; + Ast_Decl *var = ast_new(Ast_Decl, AST_VAR, node->cond->pos, AST_DECL); + var->state = DECL_RESOLVED; + var->type = type_pointer(op.type->base); + var->name = pctx->intern_it; + var->unique_name = var->name; + insert_into_scope(node->scope, var); + node->array_traversal_var = var; + } + else if (!is_bool(op.type)) { + compiler_error(node->pos, "Invalid type of for loop condition %Q, required [Bool]", typestring(op.type)); + } + else { + try_propagating_resolved_type_to_untyped_literals(node->cond, pctx->type_bool); + } + } + + resolve_expr(node->iter, AST_CAN_BE_NULL, 0, 0); + For(node->scope->stmts) resolve_stmt(it, ret); + BREAK(); + } + + // @todo: maybe add else kind ?? and then make sure other then else are AST_CANT_BE_NULL + CASE(IF, If) { + For(node->ifs) { + resolve_stmt(it->init, ret); + resolve_and_require_bool("Conditional in a if condition", it->expr, AST_CAN_BE_NULL); + try_propagating_resolved_type_to_untyped_literals(it->expr, pctx->type_bool); + For2(it->scope->stmts, jt) + resolve_stmt(jt, ret); + } + BREAK(); + } + + CASE(SWITCH, Switch) { + Operand base = resolve_expr(node->value, AST_CANT_BE_NULL, 0, 0); + if (!is_int(base.type) && !is_enum(base.type)) + compiler_error(node->pos, "You can only switch on integer values(enums included), the type of expression in switch statement is instead %Q", typestring(base.type)); + + try_converting_untyped_to_default_type(&base.value); + try_propagating_resolved_type_to_untyped_literals(node->value, base.type); + For(node->cases) { + For2(it->labels, label) { + Operand op = resolve_expr(label, AST_CANT_BE_NULL, 0, 0); + if (!op.is_const) compiler_error(label->pos, "Switch label required to be constant"); + make_sure_value_is_compatible_with_type(label->pos, &op, base.type, TYPE_AND_EXPR_REQUIRED); + try_propagating_resolved_type_to_untyped_literals(label, base.type); + } + + For2(it->scope->stmts, stmt) + resolve_stmt(stmt, ret); + } + if (node->default_scope) + For2(node->default_scope->stmts, stmt) + resolve_stmt(stmt, ret); + + BREAK(); + } + + CASE(VAR_UNPACK, Var_Unpack) { + Operand expr_op = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, 0); + if (!is_struct_union(expr_op.type)) + compiler_error(node->pos, "Expected expression to have either [Struct] or [Union] type"); + if (expr_op.type->agg.members.len != node->vars.len) + compiler_error(node->pos, "Different count of return values and assigning values"); + node->resolved_type = expr_op.type; + + For(node->vars) { + S64 index = node->vars.get_index(it); + Ast_Resolved_Member *type = expr_op.type->agg.members.data + index; + it->type = type->type; + resolve_decl(it); + insert_into_scope(node->parent_scope, it); + } + BREAK(); + } + + default: { + if (is_flag_set(ast->flags, AST_EXPR)) { + assert(is_flag_set(ast->flags, AST_STMT)); + resolve_expr((Ast_Expr *)ast, AST_CANT_BE_NULL, 0, 0); + } + else invalid_codepath; + } + } +} + +CORE_Static Ast_Type * +resolve_lambda_type(Ast_Lambda *lambda) { + Scoped_Arena scratch(pctx->scratch); + Array args = {scratch.arena}; + Ast_Type *ret = resolve_typespec(lambda->ret, AST_CAN_BE_NULL); + if (!ret) ret = pctx->type_void; + + For(lambda->args) { + if (it->name == pctx->intern("..."_s)) { + it->type = pctx->type_vargs; + + For2(lambda->args, arg) { + if (arg->expr != 0) compiler_error(arg->pos, "Default values in variadic functions are not allowed"); + } + } + else { + Ast_Type *type = resolve_typespec(it->typespec, AST_CANT_BE_NULL); + Operand default_value = resolve_expr(it->expr, AST_CAN_BE_NULL, type, 0); + make_sure_value_is_compatible_with_type(it->pos, &default_value, type, EXPR_CAN_BE_NULL); + + it->type = type; + try_propagating_resolved_type_to_untyped_literals(it->expr, it->type); + } + + args.add(it->type); + } + + return type_lambda(lambda, ret, args); +} + +CORE_Static void +try_resolving_lambda_scope(Operand *op, Ast_Lambda *lambda, Ast_Type *lambda_type) { + if (lambda->scope) { + For(lambda->args) { + insert_into_scope(lambda->scope, it); + } + For(lambda->scope->stmts) { + resolve_stmt(it, lambda_type->func.ret); + } + + *op = operand_lambda(lambda_type); + + // + // We remove all declarations to cleanup the scope + // for iteration + // + // :NestedDeclarations + // If I want to implement nested declarations + // then probably this will need to get reverted + free_all_nodes(&lambda->scope->decls); + } + else if (is_flag_set(lambda->flags, AST_FOREIGN)) { + *op = operand_lambda(lambda_type); + } +} + +CORE_Static Operand +resolve_cast(Ast_Binary *node) { + Operand expr = resolve_expr(node->left, AST_CANT_BE_NULL, 0, 0); + Ast_Type *type = resolve_typespec(node->right, AST_CANT_BE_NULL); + Ast_Type *original_type = expr.type; + node->before_type = expr.type; + + switch (expr.type->kind) { + case TYPE_TYPE: { + if (is_int(type)) { + expr.type = type; + } + else goto failure; + } break; + case TYPE_ENUM: { + if (is_int(type)) + expr.type = type; + else goto failure; + } break; + case TYPE_ARRAY: { + Ast_Type *array_base = expr.type->base; + Ast_Type *pointer_base = type_pointer(array_base); + if (pointer_base == type) { + expr.type = pointer_base; + } + else goto failure; + } break; + case TYPE_POINTER: { + if (is_pointer(type)) + expr = operand_rvalue(type); + else if (is_int(type)) + expr = operand_rvalue(type); + else goto failure; + } break; + CASE_UNTYPED : { + convert_untyped_to_typed(node->pos, &expr.value, type); + } break; + CASE_UINT: + CASE_SINT : { + if (is_int(type)) + expr.type = type; + else if (is_float(type)) { + expr.value.type = type; + if (expr.is_const) expr.value.f64_val = bigint_as_float(&expr.big_int_val); // @leak + } + else goto failure; + } break; + case TYPE_F32: + case TYPE_F64: { + if (is_float(type)) { + expr.type = type; + } + else if (is_int(type)) { + if (expr.is_const) expr.value.big_int_val = bigint_s64((S64)expr.value.f64_val); // @todo: What to do here??? + expr.type = type; + } + else goto failure; + } break; + default: + failure: + compiler_error(node->pos, "Failed to cast from %Q to %Q", typestring(expr.type), typestring(type)); + ; + } + + assert(original_type != type ? expr.type == type : 1); + if (expr.is_const) { + check_value_bounds(node->pos, &expr.value); + rewrite_into_const(node, Ast_Binary, expr.value); + } + + node->resolved_type = expr.type; + if (!expr.is_const) + try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type); + return expr; +} + +CORE_Static void +resolve_compound_array(Ast_Call *node, Ast_Type *type) { + Ast_Type *item_type = type->arr.base; + S64 size = type->arr.size; + + if (is_array(type) && node->exprs.len > size) + compiler_error(node->pos, "Too many items in compound expression, expected: %lld got: %lld", size, node->exprs.len); + + int default_counter = 0; + For(node->exprs) { + if (is_flag_set(it->call_flags, CALL_NAME)) + compiler_error(it->pos, "Arrays can't have named compound expression arguments"); + + if (is_flag_set(it->call_flags, CALL_INDEX)) { + default_counter = -1; + Operand op = require_const_int(it->index, AST_CANT_BE_NULL); + S64 i = bigint_as_signed(&op.value.big_int_val); // @todo: what happens when big num is here??? + + if (i >= size) + compiler_error(it->pos, "Index %lld is out of range of array of size %lld", i, size); + it->resolved_index = (S32)i; + } + else { + if (default_counter == -1) + compiler_error(it->pos, "Mixing expicit indexes and default is illegal"); + it->resolved_index = (S32)default_counter; + } + + Operand item = resolve_expr(it->item, AST_CANT_BE_NULL, item_type, 0); + make_sure_value_is_compatible_with_type(it->pos, &item, item_type, TYPE_AND_EXPR_REQUIRED); + it->resolved_type = item_type; + try_propagating_resolved_type_to_untyped_literals(it->item, it->resolved_type); + default_counter += 1; + } +} + +CORE_Static void +resolve_compound_struct(Ast_Call *node, Ast_Type *type) { + if (node->exprs.len != 1 && is_union(type)) compiler_error(node->pos, "Too many union initializers. Only 1 named initializer required."); + + int counter = 0; + bool named_field_appeared = false; + For(node->exprs) { + if (is_union(type) && !is_flag_set(it->call_flags, CALL_NAME)) compiler_error(it->pos, "Unions can only be initialized using named fields"); + if (is_flag_set(it->call_flags, CALL_INDEX)) compiler_error(it->pos, "Index specifier in a struct compound expression is not legal"); + if (is_flag_set(it->call_flags, CALL_NAME)) named_field_appeared = true; + if (!is_flag_set(it->call_flags, CALL_NAME) && named_field_appeared) compiler_error(it->pos, "Mixing positional and named fields in compound expressions is illegal"); + if (counter >= type->agg.members.len) compiler_error(it->pos, "Too many struct initializers"); + counter += 1; + } + + int default_counter = 0; + For(node->exprs) { + Ast_Type *item_type = 0; + if (is_flag_set(it->call_flags, CALL_NAME)) { + For2(type->agg.members, m) { + if (it->name->intern_val == m.name) { + it->resolved_name = m.name; + it->resolved_index = (S32)type->agg.members.get_index(m); + + if (m.type == pctx->type_type) { + item_type = m.type_val; + } + else { + item_type = m.type; + } + + if (m.visited) { + compiler_error(it->pos, "Field already initialized"); + } + else { + m.visited = true; + } + break; + } + } + if (!item_type) compiler_error(it->pos, "No member with that name"); + } + else { + Ast_Resolved_Member *m = &type->agg.members[default_counter]; + it->resolved_name = m->name; + it->resolved_index = (S32)default_counter; + m->visited = true; + + if (m->type == pctx->type_type && m->type_val) { + item_type = m->type_val; + } + else { + item_type = m->type; + } + + default_counter += 1; + } + + assert(item_type); + Operand item = resolve_expr(it->item, AST_CANT_BE_NULL, item_type, 0); + make_sure_value_is_compatible_with_type(it->pos, &item, item_type, TYPE_AND_EXPR_REQUIRED); + + it->resolved_type = item_type; + try_propagating_resolved_type_to_untyped_literals(it->item, it->resolved_type); + } + + For(type->agg.members) it.visited = false; +} + +CORE_Static Ast_Expr * +unpack_ast_call_expr_for_builtin(Ast_Call *call) { + if (call->exprs.len != 1) { + compiler_error(call->pos, "Expected exactly 1 argument inside a builtin function call got instead %d", (int)call->exprs.len); + } + return call->exprs[0]->item; +} + +CORE_Static bool +expr_atom_is_equal_intern(Ast_Expr *expr, Intern_String intern) { + assert(expr->kind == AST_IDENT || expr->kind == AST_BINARY || expr->kind == AST_VALUE); + if (expr->kind == AST_IDENT) { + Ast_Atom *atom = (Ast_Atom *)expr; + if (atom->intern_val == intern) { + return true; + } + } + return false; +} + +CORE_Static Operand +resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_and_const_string_context, Ast_Scope *field_access_scope) { + if (!ast && is_flag_set(flags, AST_CAN_BE_NULL)) return {}; + assert(is_flag_set(ast->flags, AST_EXPR)); + assert(ast->parent_scope->kind == AST_SCOPE || ast->parent_scope->kind == AST_FILE); + if (ast->flags & AST_COMPILER_BREAKPOINT) Breakpoint; + + switch (ast->kind) { + + CASE(IDENT, Atom) { + Ast_Scope *scope = field_access_scope ? field_access_scope : node->parent_scope; + Search_Flag flag = field_access_scope ? SEARCH_ONLY_CURRENT_SCOPE : 0; + + // @consider: Maybe instead of using AST_IDENT another case should be + // added like AST_RESOLVED or something because currently the identifier + // can already be resolved and stuff like that + + // if (node->intern_val == pctx->intern("MAP"_s)) { + // __debugbreak(); + // } + + // When copying polymorphs we fill out resolved_decl in + // identifiers, so it can happen that we have already resolved the name + Ast_Decl *decl = node->resolved_decl; + if (!decl) decl = resolve_name(scope, node->pos, node->intern_val, flag | RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED); + + // Substitute lambda alias + if (decl->kind == AST_CONST && decl->resolved_decl && decl->resolved_decl->kind == AST_LAMBDA) { + decl = decl->resolved_decl; + } + + node->resolved_decl = decl; + node->resolved_type = node->resolved_decl->type; + + Operand result = operand(node->resolved_decl); + if (result.is_const && decl->kind != AST_NAMESPACE) { + rewrite_into_const(node, Ast_Atom, node->resolved_decl->value); + node->resolved_decl = decl; + } + + return result; + BREAK(); + } + + CASE(VALUE, Atom) { + return operand_const_rvalue(node->value); + BREAK(); + } + + // Typespec array [32]int + CASE(ARRAY, Array) { + Operand type = resolve_expr(node->base, AST_CANT_BE_NULL, 0, 0); + Operand expr = require_const_int(node->expr, AST_CAN_BE_NULL); + if (type.type != pctx->type_type) compiler_error(node->pos, "Prefix array operator is only allowed on types"); + + if (node->expr) { + type_complete(type.type_val); + type.type_val = type_array(type.type_val, (S32)bigint_as_unsigned(&expr.big_int_val)); + } + else { + type.type_val = type_slice(type.type_val, node); + } + + // If this is a type we want to rewrite that type + // into an integer constant which should be an id + // of that type + if (!is_flag_set(flags, RESOLVE_TYPESPEC)) { + rewrite_into_const(node, Ast_Array, type.value); + } + + node->resolved_type = type.type_val; + return operand_type(node->resolved_type); + BREAK(); + } + + CASE(LAMBDA_EXPR, Lambda) { + node->resolved_type = resolve_lambda_type(node); + Operand result = operand_type(node->resolved_type); + try_resolving_lambda_scope(&result, node, node->resolved_type); + return result; + BREAK(); + } + + CASE(INDEX, Index) { + Operand left = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, field_access_scope); + Operand index = resolve_expr(node->index, AST_CANT_BE_NULL, 0, 0); + if (!(is_int(index.type) || is_enum(index.type))) + compiler_error(node->pos, "Trying to index the array with invalid type, expected [Int] got instead %Q", typestring(index.type)); + + node->index_original_type = left.type; + node->resolved_type = left.type->arr.base; + if (left.type == pctx->type_pointer_to_char) { + node->resolved_type = pctx->type_char; + } + if (left.type == pctx->type_pointer_to_void) { + compiler_error(node->pos, "Trying to index a void pointer is invalid"); + } + else if (is_string(left.type)) { + + // @warning: not sure about this context thing here. + // I didn't test it much so might need to delete later. + if (is_untyped(left.type)) { + if (compound_and_const_string_context) { + if (!is_string(compound_and_const_string_context)) { + compiler_error(node->pos, "Type mismatch, it's %Q but expected %Q", typestring(left.type), typestring(compound_and_const_string_context)); + } + left.type = compound_and_const_string_context; + } + else { + left.type = pctx->type_string; + } + } + node->resolved_type = pctx->type_u8; + } + + try_propagating_resolved_type_to_untyped_literals(node->index, pctx->type_int); + try_propagating_resolved_type_to_untyped_literals(node->expr, left.type); + + if (!left.type) + compiler_error(node->expr->pos, "Internal compiler error: type of array is null somehow"); + if (is_untyped(left.type)) + compiler_error(node->expr->pos, "Internal compiler error: type of array is marked as untyped somehow"); + + if (is_string(left.type)) { + return operand_lvalue_set_flag_on_node(pctx->type_u8, node); + } + + if (!is_array(left.type) && !is_pointer(left.type) && !is_slice(left.type)) { + compiler_error(node->pos, "Indexing variable that is not an [Array] or [Pointer], it's of type %Q instead", typestring(left.type)); + } + + return operand_lvalue_set_flag_on_node(node->resolved_type, node); + BREAK(); + } + + CASE(BINARY, Binary) { + if (token_is_assign(node->op)) { + assert(is_flag_set(node->flags, AST_STMT)); + Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); + if (!left.is_lvalue) compiler_error(node->pos, "Assigning to rvalue"); + Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, left.type, field_access_scope); + + // @warning: Could be buggy, previously we strictly matched if types are exact + // need to test this with slices and stuff + make_sure_value_is_compatible_with_type(node->pos, &right, left.type, TYPE_AND_EXPR_REQUIRED); + + node->resolved_type = right.type; + try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type, node->right->resolved_type); + try_propagating_resolved_type_to_untyped_literals(node->right, node->resolved_type, node->left->resolved_type); + return {}; + } + else if (node->op == TK_Arrow) { + return resolve_cast(node); + } + else if (node->op == TK_Dot) { + Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); + Ast_Scope *scope = 0; + + Ast_Decl *decl = left.resolved_decl; + if (decl && decl->kind == AST_NAMESPACE) { + scope = decl->scope; + } + + else { + // Make sure it's a type but also not a type id + Ast_Type *type = left.type; + if (type == pctx->type_type && left.type_val) { + if (is_enum(left.type_val) || is_struct(left.type_val) || is_union(left.type_val)) { + type = left.type_val; + } + } + + // We need 2 resolved types, for left and right + // Then we can compare if we got a pointer we need to + // figure that out to replace '.' with '->' for pointer structs + node->dot_access_step_resolution = type; + + if (is_pointer(type)) type = type->base; + type_complete(type); + + bool is_dottable_type = type->kind == TYPE_ENUM || type->kind == TYPE_STRUCT || type->kind == TYPE_UNION; + if (is_dottable_type == false) compiler_error(node->pos, "Type %Q doesn't have anything to access using '.' operator", typestring(type)); + + scope = ((Ast_Decl *)type->ast)->scope; + if (!scope) { + compiler_error(node->pos, "Internal compiler error? Type %Q doesn't have scope, you cannot use '.' on this variable", typestring(type)); + } + } + + Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, scope); + node->resolved_type = right.type; + + if (right.is_const) { + rewrite_into_const(node, Ast_Binary, right.value); + } + return right; + } + else { + Operand left = resolve_expr(node->left, AST_CANT_BE_NULL, 0, field_access_scope); + Operand right = resolve_expr(node->right, AST_CANT_BE_NULL, 0, field_access_scope); + B32 is_const = left.is_const && right.is_const; + B32 proceed_to_default_operator_handler = true; + + // Try finding a operator overload + if (!is_const) { + Value left_copy = left.value; + Value right_copy = right.value; + try_converting_untyped_to_default_type(&left_copy); + try_converting_untyped_to_default_type(&right_copy); + U64 hash = calculate_hash_for_arguments(left_copy.type, right_copy.type); + Ast_Decl *operator_overload = resolve_operator_overload(node->parent_scope, left_copy.type, right_copy.type, node->pos, node->op, hash); + + if (operator_overload) { + proceed_to_default_operator_handler = false; + left.value = left_copy; + right.value = right_copy; + // @warning: might be buggy, added after a long break + // Propagate const + if (left.is_const) { + Ast_Atom *atom_left = (Ast_Atom *)node->left; + atom_left->value = left.value; + } + if (right.is_const) { + Ast_Atom *atom_right = (Ast_Atom *)node->right; + atom_right->value = right.value; + } + + node->resolved_type = operator_overload->type->func.ret; + node->resolved_operator_overload = operator_overload; + + // We opt out early because we convert all literals to default type + // We don't need to propagate resolved type + return operand_rvalue(node->resolved_type); + } + } + + // Not found or constant then go for default option + Value value = {}; + if (proceed_to_default_operator_handler) { + value = eval_binary(node->pos, node->op, left.value, right.value, is_const); + node->resolved_type = value.type; + } + + if (is_const) { + // We don't need to propagte types for const values cause we are rewritting them + rewrite_into_const(node, Ast_Binary, value); + return operand_const_rvalue(value); + } + else { + try_propagating_resolved_type_to_untyped_literals(node->left, node->resolved_type); + try_propagating_resolved_type_to_untyped_literals(node->right, node->resolved_type); + return operand_rvalue(node->resolved_type); + } + } + BREAK(); + } + + CASE(VAR, Decl) { + resolve_stmt(node, 0); + return {}; + BREAK(); + } + + CASE(UNARY, Unary) { + Operand value = resolve_expr(node->expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + if (node->op == TK_Pointer) { + if (value.type->kind == TYPE_POINTER) { + node->resolved_type = value.type->base; + return operand_lvalue_set_flag_on_node(node->resolved_type, node); + } + else if (value.type->kind == TYPE_TYPE) { + value.type_val = type_pointer(value.type_val); + if (!is_flag_set(flags, RESOLVE_TYPESPEC)) { + rewrite_into_const(node, Ast_Unary, value.value); + } + + node->resolved_type = value.type_val; + return operand_type(node->resolved_type); + } + else { + compiler_error(node->pos, "Dereferencing expression %Q that is not a [Pointer] or [Type]", typestring(value.type)); + return {}; + } + } + else if (node->op == TK_Dereference) { + if (!value.is_lvalue) { + compiler_error(node->pos, "Can't take a pointer of this expression. It's not bound to anything so taking a pointer of it would be pretty silly."); + } + + node->resolved_type = type_pointer(value.type); + return operand_lvalue_set_flag_on_node(node->resolved_type, node); + } + else { + + // Try finding a operator overload + B32 proceed_to_default_operator_handler = true; + if (!value.is_const) { + U64 hash = calculate_hash_for_arguments(value.type); + Ast_Decl *operator_overload = resolve_operator_overload(node->parent_scope, value.type, 0, node->pos, node->op, hash); + if (operator_overload) { + proceed_to_default_operator_handler = false; + node->resolved_type = operator_overload->type->func.ret; + node->resolved_operator_overload = operator_overload; + } + } + + if (proceed_to_default_operator_handler) { + eval_unary(node->pos, node->op, &value); + node->resolved_type = value.type; + } + + if (value.is_const) { + rewrite_into_const(node, Ast_Unary, value.value); + return operand_const_rvalue(value.value); + } + + return operand_rvalue(node->resolved_type); + } + + BREAK(); + } + + CASE(COMPOUND, Call) { + Operand op = resolve_expr(node->typespec, inherit_flag(flags, AST_CAN_BE_NULL), 0, field_access_scope); + + Ast_Type *type = op.type; + if (type) { + if (type != pctx->type_type) + compiler_error(node->pos, "Expected type [Type] got instead %Q", typestring(type)); + type = op.type_val; + } + if (!type) + type = compound_and_const_string_context; + if (type == pctx->type_void || type == nullptr) + compiler_error(node->pos, "Couldn't infer type of compound expression"); + + type_complete(type); + node->resolved_type = type; + if (is_array(type) || is_slice(type)) { + resolve_compound_array(node, type); + } + else if (is_struct(type) || is_union(type)) { + resolve_compound_struct(node, type); + } + else { + compiler_error(node->pos, "Internal compiler error: Invalid type was passed to the compound expression, should have been an array, struct or slice"); + } + + return operand_lvalue_set_flag_on_node(type, node); + + BREAK(); + } + + CASE(CALL, Call) { + if (expr_atom_is_equal_intern(node->name, pctx->intern_sizeof)) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + + Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + node->kind = AST_SIZE_OF; + + Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; + type_complete(type); + + if (type->size == 0) { + compiler_error(node->pos, "Internal compiler error: calling sizeof but the resulting size of type is obviously invalid suggesting that type was not completed properly"); + } + + Value v = value_int(type->size); + rewrite_into_const(node, Ast_Builtin, v); + return operand_const_rvalue(v); + } + + else if (expr_atom_is_equal_intern(node->name, pctx->intern_typeof)) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + + Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + node->kind = AST_TYPE_OF; + Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; + + Operand result = operand_type(type); + rewrite_into_const(node, Ast_Builtin, result.value); + return result; + } + + else if (expr_atom_is_equal_intern(node->name, pctx->intern_alignof)) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + node->kind = AST_ALIGN_OF; + + Ast_Type *type = name.type == pctx->type_type ? name.type_val : name.type; + type_complete(type); + if (type->size == 0) { + compiler_error(node->pos, "Internal compiler error: calling sizeof but the resulting size of type is obviously invalid suggesting that type was not completed properly"); + } + + Value v = value_int(type->align); + rewrite_into_const(node, Ast_Builtin, v); + return operand_const_rvalue(v); + } + + else if (expr_atom_is_equal_intern(node->name, pctx->intern_len)) { + Ast_Expr *expr = unpack_ast_call_expr_for_builtin(node); + + Operand name = resolve_expr(expr, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + node->kind = AST_LENGTH_OF; + node->resolved_type = pctx->type_int; + if (is_array(name.type)) { + Value value = value_int(name.type->arr.size); + rewrite_into_const(node, Ast_Builtin, value); + return operand_const_rvalue(value); + } + else if (name.type->kind == TYPE_UNTYPED_STRING) { + Value value = value_int(name.intern_val.len); + rewrite_into_const(node, Ast_Builtin, value); + return operand_const_rvalue(value); + } + else if (is_array(name.type) || is_slice(name.type) || is_string(name.type)) { + return operand_rvalue(pctx->type_int); + } + else compiler_error(node->pos, "Can't get length of type %Q", typestring(name.type)); + } + + else { + Operand name = resolve_expr(node->name, inherit_flag(flags, AST_CANT_BE_NULL), 0, field_access_scope); + + if (name.resolved_decl->flags & AST_POLYMORPH) { + assert(name.resolved_decl->flags & AST_PARENT_POLYMORPH); + Ast_Decl *poly = name.resolved_decl; + + if (poly->kind == AST_STRUCT || poly->kind == AST_UNION) { + Ast_Decl *instance = get_or_instantiate_polymorph_type(node->pos, poly, node->exprs, field_access_scope); + return operand_type(instance->type_val); + } + } + + /* + We need an algorithm and other concretes for correct matching of arrays, var args and others. + + (a: int = 5, b: int) Disallowed, this is not lua where table has indexed and keyed values at the same time + (a: int = 5, b: int = 10) + (a = 2, b = 10) + (32, 4) + (32, b = 32) + (32, a = 32) Error + (b = 3, b = 4) Error + + (a: int, b: ..String) + (a: int, b: ..Any) + (10, 10, 10) + (10, b = {10, 10, 10, 10}) + (10, ..slice) // We want to avoid accidental slice pass + (10, b = ..slice) + (a = 10, 10, 10) error, there shouldn't be normal args after named + + + (a: int, b: ..#vargs) // Var args will banish all forms of named arguments + (a = 10, b = 10) error + (b = 10, 10) error + (10, 10) OK + Any form of named arguments is invalid + + + (a: int, b: []Any) + (a = 10, b = {1, 2, "asd"}) + (a: int, b: []String) + + */ + + // + // Regular call + // + if (!name.resolved_decl) { + compiler_error(node->pos, "Internal compiler error: Failed to propagate a resolved lambda declaration from atom resolution"); + } + if (name.resolved_decl->kind != AST_LAMBDA) { + compiler_error(node->pos, "Calling %Q which is not a [Lambda]", typestring(name.type)); + } + node->resolved_decl = name.resolved_decl; + + /* + @warning + We only have one instance of a given Lambda type for example (a: Vec3): Vec3. + Even though we might have multiple functions with this signature. + This creates a problem cause sometimes we might refer to the wrong Ast. + Need to be careful!!! + */ + + For(node->exprs) { + if (is_flag_set(it->call_flags, CALL_INDEX)) { + compiler_error(it->pos, "Index inside a call is not allowed!"); + } + } + + Scoped_Arena scratch(pctx->scratch); + Ast_Lambda *lambda = name.resolved_decl->lambda; + struct Match { + Ast_Decl *lambda_arg; + Ast_Call_Item *call_arg; + }; + Array matches = {scratch.arena}; + S64 default_iter = 0; + + // For every defined argument in the lambda we seek for + // a corresponding item in the call expression. + // + // This algorithm is cool because it results in correct argument order. + // It's kind of complicated, especially the default_iter stuff is kind + // of weird but it works very well. The default_iter is only incremented + // on successful match because we are iterating through entire call expression + // every time. + // Might be wise to revise it later but the approach of starting with + // lambda_args seems correct. + For2(lambda->args, lambda_arg) { + // Variadic functions cannot have default arguments + // we make sure of that when resolving the lambda + if (lambda_arg->kind == AST_VARGS_LAMBDA_PARAM) { + For(node->exprs) { + if (is_flag_set(it->call_flags, CALL_NAME)) { + compiler_error(it->pos, "Variadic function doesnt allow for named arguments!"); + } + + S64 idx = node->exprs.get_index(it); + if (idx >= default_iter) { + matches.add({lambda_arg, it}); + } + } + break; + } + + Ast_Call_Item *item = 0; + For(node->exprs) { + S64 it_index = node->exprs.get_index(it); + + bool named_argument = is_flag_set(it->call_flags, CALL_NAME); + bool named_argument_already_appeared = it_index > default_iter; + if (named_argument) { + Ast_Atom *name = it->name; + assert(name->kind == AST_IDENT); + if (name->intern_val.str == lambda_arg->name.str) item = it; + } + + else if (it_index == default_iter) { + default_iter += 1; + item = it; + } + + else if (named_argument_already_appeared) compiler_error(it->pos, "Positional argument after named argument is not permitted"); + + if (item) break; + } + + // If we couldn't find corresponding call item we should seek + // for a default value inside the lambda declaration + if (!item) { + if (!lambda_arg->expr) compiler_error(node->pos, node->resolved_decl->pos, "Required value: %Q in lambda call was not passed", lambda_arg->name); + item = ast_call_item(lambda_arg->expr->pos, 0, 0, lambda_arg->expr); + item->resolved_type = lambda_arg->expr->resolved_type; + } + matches.add({lambda_arg, item}); + } + + // Make sure we found every item. + For2(node->exprs, call_it) { + bool included = false; + For2(matches, match_it) { + if (call_it == match_it.call_arg) { + included = true; + break; + } + } + if (!included) compiler_error(call_it->pos, "Unknown argument to a function call, couldn't match it with any of the declared arguments"); + } + + // Find the polymorph + if (lambda->flags & AST_POLYMORPH) { + Array replacements = {scratch.arena}; + ForArrayRemovable(matches) { + ForArrayRemovablePrepare(matches); + if (it.lambda_arg->flags & AST_POLYMORPH) replacements.add(it.call_arg); + if (it.lambda_arg->flags & AST_IDENT_POLYMORPH) ForArrayRemovableDeclare(); + } + + Ast_Decl *poly = name.resolved_decl; + Ast_Decl *instance = get_or_instantiate_polymorph_lambda(node->pos, poly, replacements, field_access_scope); + node->resolved_decl = instance; + lambda = instance->lambda; + + // We now need to fix the matches list CAUSE ITS FUCKED + // Thanks polymorphism + For2(matches, match_it) { + For2(lambda->args, lambda_it) { + if (match_it.lambda_arg->name == lambda_it->name) { + match_it.lambda_arg = lambda_it; + break; + } + } + } + } + + // Typecheck the arguments and produce properly ordered list of arguments for codegeneration + Array items = {scratch.arena}; + For(matches) { + Ast_Call_Item *item = it.call_arg; + Ast_Decl *lambda_arg = it.lambda_arg; + if (lambda_arg->flags & AST_IDENT_POLYMORPH) continue; + if (lambda_arg->kind == AST_VARGS_LAMBDA_PARAM) { + Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type, field_access_scope); + try_converting_untyped_to_default_type(&expr); + if (expr.type == pctx->type_string) expr.type = pctx->type_pointer_to_char; + item->resolved_type = expr.type; + } + else { + Operand expr = resolve_expr(item->item, AST_CANT_BE_NULL, lambda_arg->type, field_access_scope); + make_sure_value_is_compatible_with_type(item->pos, &expr, lambda_arg->type, TYPE_AND_EXPR_REQUIRED); + item->resolved_type = lambda_arg->type; + } + try_propagating_resolved_type_to_untyped_literals(item->item, item->resolved_type); + items.add(item); + } + node->exprs = items.tight_copy(pctx->perm); + + node->resolved_type = lambda->resolved_type->func.ret; + return operand_rvalue(node->resolved_type); + // + // CALL End + // + } + BREAK(); + } + + invalid_default_case; + } + + invalid_return; +} + +CORE_Static Ast_Type * +get_type_base(Ast_Type *type) { + switch (type->kind) { + case TYPE_POINTER: + case TYPE_SLICE: + case TYPE_ARRAY: return get_type_base(type->base); + default: return type; + } +} + +CORE_Static void +resolve_name_for_global_decl(Ast_Decl *node) { + if (node->kind == AST_LAMBDA && is_flag_set(node->expr->flags, AST_FOREIGN)) return; // @cleanup ? + if (is_flag_set(node->flags, AST_FOREIGN)) return; + if (!is_flag_set(node->flags, AST_GLOBAL)) return; + + Ast_Scope *s = node->parent_scope; + Intern_String namespace_name = {}; + for (; s; s = s->parent_scope) { + if (s->first_namespace_name.len) { + namespace_name = s->first_namespace_name; + break; + } + } + if (namespace_name.len) { + node->unique_name = pctx->internf("%Q_%Q", namespace_name, node->unique_name); + } + // printf("%.*s\n", (int)node->unique_name.len, node->unique_name.str); +} + +CORE_Static void +resolve_decl(Ast_Decl *ast) { + if (ast->state == DECL_RESOLVED) + return; + else if (ast->state == DECL_RESOLVING) { + compiler_error(ast->pos, "Cyclic dependency of %s", ast->name.str); + return; + } + if (ast->flags & AST_PARENT_POLYMORPH) return; + assert(ast->state == DECL_NOT_RESOLVED); + + ast->state = DECL_RESOLVING; + Ast_Decl *node = (Ast_Decl *)ast; + node->unique_name = node->name; + if (ast->flags & AST_COMPILER_BREAKPOINT) Breakpoint; + { + switch (ast->kind) { + case AST_LAMBDA: { + Ast_Lambda *lambda = node->lambda; + lambda->resolved_type = resolve_lambda_type(lambda); + Operand result = operand_type(lambda->resolved_type); + + // @note: top level lambda needs to get marked as resolved + // so that the cyclic dependency wont trigger + node->type = lambda->resolved_type; + node->state = DECL_RESOLVED; + + // @todo: We also need to make sure there is a return value when ret type is not void + try_resolving_lambda_scope(&result, lambda, node->type); + node->value = result.value; + + Scoped_Arena scratch(pctx->scratch); + + if (is_flag_set(node->flags, AST_OPERATOR_OVERLOAD)) { + node->unique_name = pctx->internf("OPERATOR_%Q%d", node->overload_op_info->name, pctx->global_decl_ids++); + } + else { + if (node->flags & AST_POLYMORPH_INSTANCE) { + node->unique_name = pctx->internf("%Q%d", node->name, pctx->global_decl_ids++); + } + resolve_name_for_global_decl(node); + } + } break; + + case AST_CONST: { + // @warning: if in the future we add type here then pass it to resolve expr for + // compound + Operand op = resolve_expr(node->expr, AST_CANT_BE_NULL, 0, 0); + if (!op.is_const) { + compiler_error(node->pos, "Assigning a value that is not constant to a constant declaration"); + } + + node->value = op.value; + if (op.value.type == pctx->type_type) { + node->kind = AST_TYPE; + if (is_flag_set(node->flags, AST_STRICT)) { + node->type_val = type_copy(pctx->perm, node->type_val); + } + } + resolve_name_for_global_decl(node); + + break; + } + + case AST_VAR: { + Ast_Type *type = node->type; + if (!type) type = resolve_typespec(node->typespec, AST_CAN_BE_NULL | RESOLVE_TYPESPEC_COMPLETE); + Operand op = resolve_expr(node->expr, AST_CAN_BE_NULL, type, 0); + assert(op.type != 0 || type != 0); + + make_sure_value_is_compatible_with_type(node->pos, &op, type, EXPR_CAN_BE_NULL | TYPE_CAN_BE_NULL); + node->value = op.value; + + if (is_flag_set(node->flags, AST_FOREIGN)) { + if (node->expr) compiler_error(node->pos, "#foreign variable shouldn't have value"); + } + + try_propagating_resolved_type_to_untyped_literals(node->expr, node->type); + + if (op.is_const) { + set_flag(node->flags, AST_VAR_IS_CONST); + } + // if (node->name == pctx->internf("CurrentMap")) __debugbreak(); + resolve_name_for_global_decl(node); + break; + } + + case AST_NAMESPACE: + break; + + case AST_ENUM: { + Ast_Type *type_of_enum = resolve_typespec(node->typespec, AST_CAN_BE_NULL); + + node->type = pctx->type_type; + node->type_val = type_enum(node, type_of_enum); + + S64 value = 1; + For2(node->scope->decls, decl) { + Operand op = {}; + if (decl->expr) { + op = require_const_int(decl->expr, AST_CANT_BE_NULL); + value = bigint_as_signed(&op.big_int_val) + 1; + } + else { + decl->state = DECL_RESOLVED; + op.type = node->type_val; + bigint_init_signed(&op.big_int_val, value); + if (is_flag_set(node->flags, AST_FLAG)) { + value = value << 1; + } + else { + value += 1; + } + } + + decl->value = op.value; + } + resolve_name_for_global_decl(node); + } break; + + invalid_default_case; + } + } + ast->state = DECL_RESOLVED; + + bool is_polymorph = is_flag_set(ast->flags, AST_POLYMORPH); + bool is_global = is_flag_set(ast->flags, AST_GLOBAL); + if (is_global && !is_polymorph) { + add(pctx->perm, &pctx->ordered_decls, ast); + } +} diff --git a/core_typechecking.h b/src/language/core_typechecking.h similarity index 96% rename from core_typechecking.h rename to src/language/core_typechecking.h index 016e25d..9c83108 100644 --- a/core_typechecking.h +++ b/src/language/core_typechecking.h @@ -1,71 +1,71 @@ - - -struct Operand { - /*#import meta -meta.inline_value_fields() - */ - union { - Value value; - struct { - Ast_Type *type; - Ast_Decl *resolved_decl; - union { - bool bool_val; - double f64_val; - Intern_String intern_val; - BigInt big_int_val; - Ast_Type *type_val; - }; - }; - }; - /*END*/ - // is_const is used to rewrite the tree and bound - // something to the const name at the end - U8 is_const : 1; - U8 is_lvalue : 1; - U8 pound_strict : 1; -}; - -struct Scope_Search { - Array results; - Intern_String name; - Array scopes; - - bool exit_on_find; - bool search_only_current_scope; - U32 scope_visit_id; -}; - -typedef U32 Typecheck_Flag; -enum { - TYPE_AND_EXPR_REQUIRED = 0, - TYPE_CAN_BE_NULL = 1, - EXPR_CAN_BE_NULL = 2 -}; - -typedef U32 Resolve_Flag; -enum { - AST_CANT_BE_NULL = bit_flag(0), - AST_CAN_BE_NULL = bit_flag(1), - RESOLVE_TYPESPEC_COMPLETE = bit_flag(2), - RESOLVE_TYPESPEC = bit_flag(3), -}; - -typedef U32 Search_Flag; -enum { - SEARCH_ONLY_CURRENT_SCOPE = bit_flag(1), - RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED = bit_flag(2), -}; - -CORE_Static void try_converting_untyped_to_default_type(Operand *op); -CORE_Static void try_propagating_resolved_type_to_untyped_literals(Ast *ast, Ast_Type *type, Ast_Type *additional_not_bool_type = 0); - -CORE_Static Operand resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_context, Ast_Scope *field_access_scope); -CORE_Static void resolve_decl(Ast_Decl *ast); -CORE_Static Ast_Decl *resolve_name(Ast_Scope *parent_scope, Token *pos, Intern_String name, Search_Flag search_flags = 0); -#define CASE(kind, type) \ - case AST_##kind: { \ - Ast_##type *node = (Ast_##type *)ast; -#define BREAK() \ - } \ - break + + +struct Operand { + /*#import meta +meta.inline_value_fields() + */ + union { + Value value; + struct { + Ast_Type *type; + Ast_Decl *resolved_decl; + union { + bool bool_val; + double f64_val; + Intern_String intern_val; + BigInt big_int_val; + Ast_Type *type_val; + }; + }; + }; + /*END*/ + // is_const is used to rewrite the tree and bound + // something to the const name at the end + U8 is_const : 1; + U8 is_lvalue : 1; + U8 pound_strict : 1; +}; + +struct Scope_Search { + Array results; + Intern_String name; + Array scopes; + + bool exit_on_find; + bool search_only_current_scope; + U32 scope_visit_id; +}; + +typedef U32 Typecheck_Flag; +enum { + TYPE_AND_EXPR_REQUIRED = 0, + TYPE_CAN_BE_NULL = 1, + EXPR_CAN_BE_NULL = 2 +}; + +typedef U32 Resolve_Flag; +enum { + AST_CANT_BE_NULL = bit_flag(0), + AST_CAN_BE_NULL = bit_flag(1), + RESOLVE_TYPESPEC_COMPLETE = bit_flag(2), + RESOLVE_TYPESPEC = bit_flag(3), +}; + +typedef U32 Search_Flag; +enum { + SEARCH_ONLY_CURRENT_SCOPE = bit_flag(1), + RESOLVE_NAME_MAKE_SURE_OPERATOR_OVERLOAD_IS_NOT_EVER_CALLED = bit_flag(2), +}; + +CORE_Static void try_converting_untyped_to_default_type(Operand *op); +CORE_Static void try_propagating_resolved_type_to_untyped_literals(Ast *ast, Ast_Type *type, Ast_Type *additional_not_bool_type = 0); + +CORE_Static Operand resolve_expr(Ast_Expr *ast, Resolve_Flag flags, Ast_Type *compound_context, Ast_Scope *field_access_scope); +CORE_Static void resolve_decl(Ast_Decl *ast); +CORE_Static Ast_Decl *resolve_name(Ast_Scope *parent_scope, Token *pos, Intern_String name, Search_Flag search_flags = 0); +#define CASE(kind, type) \ + case AST_##kind: { \ + Ast_##type *node = (Ast_##type *)ast; +#define BREAK() \ + } \ + break diff --git a/core_types.cpp b/src/language/core_types.cpp similarity index 97% rename from core_types.cpp rename to src/language/core_types.cpp index 87fecc4..2cb4ec1 100644 --- a/core_types.cpp +++ b/src/language/core_types.cpp @@ -1,359 +1,359 @@ - -CORE_Static const char * -get_name_of_type(Ast_Type *type) { - switch (type->kind) { - case TYPE_VOID: return "void"; - case TYPE_BOOL: return "bool"; - case TYPE_F32: return "F32"; - case TYPE_F64: return "F64"; - case TYPE_S8: return "S8"; - - case TYPE_CHAR: return "char"; - case TYPE_UCHAR: return "uchar"; - case TYPE_INT: return "int"; - case TYPE_UINT: return "uint"; - case TYPE_LONG: return "long"; - case TYPE_ULONG: return "ulong"; - case TYPE_LLONG: return "llong"; - case TYPE_ULLONG: return "ullong"; - case TYPE_SHORT: return "short"; - case TYPE_USHORT: return "ushort"; - - case TYPE_S16: return "S16"; - case TYPE_S32: return "S32"; - case TYPE_S64: return "S64"; - case TYPE_U8: return "U8"; - case TYPE_U16: return "U16"; - case TYPE_U32: return "U32"; - case TYPE_U64: return "U64"; - case TYPE_TYPE: - return "Type"; - case TYPE_POLYMORPH: - return ""; - invalid_default_case; - } - return ""; -} - -//----------------------------------------------------------------------------- -// Type constructors and utillities -//----------------------------------------------------------------------------- -force_inline B32 is_any(Ast_Type *a) { return a == pctx->type_any; } -force_inline B32 is_struct(Ast_Type *a) { return a->kind == TYPE_STRUCT; } -force_inline B32 is_union(Ast_Type *a) { return a->kind == TYPE_UNION; } -force_inline B32 is_struct_union(Ast_Type *a) { return a->kind == TYPE_UNION || a->kind == TYPE_STRUCT; } -force_inline B32 is_lambda(Ast_Type *a) { return a->kind == TYPE_LAMBDA; } -force_inline B32 is_array(Ast_Type *a) { return a->kind == TYPE_ARRAY; } -force_inline B32 is_slice(Ast_Type *a) { return a->kind == TYPE_SLICE; } -force_inline B32 is_enum(Ast_Type *a) { return a->kind == TYPE_ENUM; } -force_inline B32 is_pointer(Ast_Type *a) { return a->kind == TYPE_POINTER; } -force_inline B32 is_void(Ast_Type *a) { return a->kind == TYPE_VOID; } -force_inline B32 is_void_pointer(Ast_Type *a) { return a == pctx->type_pointer_to_void; } -force_inline B32 is_string(Ast_Type *a) { return a == pctx->type_string || a->kind == TYPE_UNTYPED_STRING || a == pctx->type_pointer_to_char; } -force_inline B32 is_untyped_int(Ast_Type *a) { return a->kind == TYPE_UNTYPED_INT; } -force_inline B32 is_typed_int(Ast_Type *a) { return (a->kind >= TYPE_S64 && a->kind <= TYPE_U8); } -force_inline B32 is_int(Ast_Type *a) { return (a->kind >= TYPE_S64 && a->kind <= TYPE_U8) || a->kind == TYPE_UNTYPED_INT; } -force_inline B32 is_signed_int(Ast_Type *a) { return !a->is_unsigned; } -force_inline B32 is_unsigned_int(Ast_Type *a) { return a->is_unsigned; } -force_inline B32 is_float(Ast_Type *a) { return a->kind == TYPE_F32 || a->kind == TYPE_F64 || a->kind == TYPE_UNTYPED_FLOAT; } -force_inline B32 is_f32(Ast_Type *a) { return a->kind == TYPE_F32; } -force_inline B32 is_f64(Ast_Type *a) { return a->kind == TYPE_F64; } -force_inline B32 is_bool(Ast_Type *a) { return a->kind == TYPE_BOOL || a->kind == TYPE_UNTYPED_BOOL; } -force_inline B32 is_untyped(Ast_Type *a) { return a->kind >= TYPE_UNTYPED_FIRST && a->kind <= TYPE_UNTYPED_LAST; } -force_inline B32 is_typed(Ast_Type *a) { return !is_untyped(a); } -force_inline B32 is_numeric(Ast_Type *type) { - return (type->kind >= TYPE_UNTYPED_FIRST_NUMERIC && type->kind <= TYPE_UNTYPED_LAST_NUMERIC) || - (type->kind >= TYPE_FIRST_NUMERIC && type->kind <= TYPE_LAST_NUMERIC); -} - -//----------------------------------------------------------------------------- -// Hash consed types -//----------------------------------------------------------------------------- -CORE_Static Ast_Type * -type_new(Allocator *allocator, Ast_Type_Kind kind, int32_t size, int32_t align, bool generate_type_id = true) { - Ast_Type *result = allocate_struct(allocator, Ast_Type, true); - result->kind = kind; - result->size = size; - result->align = align; - if (generate_type_id) result->type_id = pctx->type_ids++; - add(pctx->perm, &pctx->all_types, result); - return result; -} - -CORE_Static Ast_Type * -type_copy(Allocator *a, Ast_Type *type) { - // @warning: This changes type id !!!! - Ast_Type *result = allocate_struct(a, Ast_Type); - memory_copy(result, type, sizeof(Ast_Type)); - result->type_id = pctx->type_ids++; - add(pctx->perm, &pctx->all_types, result); - return result; -} - -CORE_Static Ast_Type * -type_pointer(Ast_Type *base) { - Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, (void *)base); - if (!result) { - result = type_new(pctx->perm, TYPE_POINTER, pointer_size, pointer_align); - result->base = base; - result->is_unsigned = true; - map_insert(&pctx->type_map, base, result); - } - assert(result->kind == TYPE_POINTER); - return result; -} - -CORE_Static Ast_Type * -type_slice(Ast_Type *base, Ast *ast) { - U64 hash_base = hash_ptr(base); - U64 hash = hash_mix(hash_base, hash_u64(ARRAY_SIZE_SLICE)); - Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); - if (result) { - assert(result->kind == TYPE_SLICE); - assert(result->arr.base == base); - return result; - } - - struct Slice { - void *p; - S64 len; - }; - result = type_new(pctx->perm, TYPE_SLICE, sizeof(Slice), alignof(Slice)); - result->arr.base = base; - result->arr.slice_hash = hash; - result->ast = ast; - map_insert(&pctx->type_map, hash, result); - return result; -} - -CORE_Static Ast_Type * -type_array(Ast_Type *base, S32 size) { - U64 hash_base = hash_ptr(base); - U64 hash = hash_mix(hash_base, hash_u64(size)); - Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); - if (result) { - assert(result->kind == TYPE_ARRAY); - assert(result->arr.size == size); - assert(result->arr.base == base); - return result; - } - - result = type_new(pctx->perm, TYPE_ARRAY, size * base->size, pointer_align); - result->arr.base = base; - result->arr.size = size; - result->arr.slice_hash = hash_mix(hash_base, hash_u64(ARRAY_SIZE_SLICE)); - map_insert(&pctx->type_map, hash, result); - return result; -} - -inline U64 calculate_hash_for_arguments(Ast_Type *a, Ast_Type *b) { - U64 result = 13; - result = hash_mix(result, hash_ptr(a)); - result = hash_mix(result, hash_ptr(b)); - return result; -} - -inline U64 calculate_hash_for_arguments(Ast_Type *a) { - U64 result = 13; - result = hash_mix(result, hash_ptr(a)); - return result; -} - -CORE_Static Ast_Type * -type_lambda(Ast *ast, Ast_Type *ret, Array args) { - U64 hash_without_ret = 13; - For(args) hash_without_ret = hash_mix(hash_without_ret, hash_ptr(it)); - U64 hash = hash_mix(hash_ptr(ret), hash_without_ret); - Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); - - if (result) { - assert(result->kind == TYPE_LAMBDA); - assert(result->func.args.len == args.len); - return result; - } - - result = type_new(pctx->perm, TYPE_LAMBDA, pointer_size, pointer_align); - result->ast = ast; - result->func.ret = ret; - result->func.args = args.tight_copy(pctx->perm); - result->func.hash_without_ret = hash_without_ret; - map_insert(&pctx->type_map, hash, result); - - return result; -} - -CORE_Static Ast_Type * -type_enum(Ast_Decl *ast, Ast_Type *type) { - if (!type) { - type = pctx->type_int; - } - - Ast_Type *result = type_new(pctx->perm, TYPE_ENUM, type->size, type->align); - result->base = type; - result->ast = ast; - return result; -} - -CORE_Static Ast_Type * -type_incomplete(Ast *ast) { - Ast_Type_Kind kind = TYPE_INCOMPLETE; - - bool generate_type_id = true; - if (is_flag_set(ast->flags, AST_POLYMORPH)) { - kind = TYPE_POLYMORPH; - generate_type_id = false; - } - - Ast_Type *result = type_new(pctx->perm, kind, 0, 0, generate_type_id); - result->ast = ast; - return result; -} - -CORE_Static void resolve_name_for_global_decl(Ast_Decl *node); -CORE_Static void type_complete(Ast_Type *type); -CORE_Static void -type_struct_complete(Ast_Type *type, Ast_Decl *node) { - assert(node->kind == AST_STRUCT || node->kind == AST_UNION); - if (is_flag_set(node->flags, AST_COMPILER_BREAKPOINT)) Breakpoint; - Scoped_Arena scratch(pctx->scratch); - - if (node->kind == AST_STRUCT) { - // First resolve and compute sizes of struct members - // - Array members = {scratch.arena}; - type->kind = TYPE_COMPLETING; - S32 members_size = 0; - For(node->scope->decls) { - resolve_decl(it); - assert(it->type->kind != TYPE_INCOMPLETE); - assert(is_pow2(it->type->align)); - - Ast_Resolved_Member m = {}; - m.offset = type->size; - members_size += it->type->size; - type->align = max(type->align, it->type->align); - type->size = it->type->size + (S32)align_up(type->size, it->type->align); - - m.name = it->name; - m.value = it->value; - members.add(m); - } - - // - // Then compute size of struct itself - // - type->size = (S32)align_up(type->size, type->align); - type->padding = type->size - members_size; - type->agg.members = members.tight_copy(pctx->perm); - type->kind = TYPE_STRUCT; - } - else { - assert(node->kind == AST_UNION); - // First resolve and compute sizes of union members - // - Array members = {scratch.arena}; - type->kind = TYPE_COMPLETING; - For(node->scope->decls) { - resolve_decl(it); - assert(it->type->kind != TYPE_INCOMPLETE); - assert(is_pow2(it->type->align)); - - type->align = max(type->align, it->type->align); - type->size = max(it->type->size, type->size); - - Ast_Resolved_Member m = {}; - m.name = it->name; - m.value = it->value; - members.add(m); - } - - // - // Then compute size of union itself - // - type->size = (S32)align_up(type->size, type->align); - type->agg.members = members.tight_copy(pctx->perm); - type->kind = TYPE_UNION; - } - resolve_name_for_global_decl(node); -} - -CORE_Static void -type_complete(Ast_Type *type) { - if (!type) { - return; - } - if (type->kind == TYPE_COMPLETING) { - compiler_error(type->ast->pos, "Cyclic type dependency"); - } - else if (type->kind != TYPE_INCOMPLETE) { - return; - } - - type_struct_complete(type, (Ast_Decl *)type->ast); - add(pctx->perm, &pctx->ordered_decls, (Ast_Decl *)type->ast); -} - -CORE_Static void -typename_base(String_Builder *sb, Ast_Type *type) { - switch (type->kind) { - case TYPE_INCOMPLETE: sb->addf("INCOMPLETE"); break; - case TYPE_COMPLETING: sb->addf("COMPLETING"); break; - case TYPE_TYPE: sb->addf("TYPE"); break; - case TYPE_POINTER: - sb->addf("*"); - typename_base(sb, type->base); - break; - case TYPE_LAMBDA: - sb->addf("("); - For(type->func.args) { - typename_base(sb, it); - if (!type->func.args.is_last(it)) sb->addf(", "); - } - - sb->addf("):"); - typename_base(sb, type->func.ret); - break; - case TYPE_ARRAY: - sb->addf("[%d]", (int)type->arr.size); - typename_base(sb, type->arr.base); - break; - case TYPE_SLICE: - sb->addf("[]"); - typename_base(sb, type->base); - break; - case TYPE_UNION: - case TYPE_STRUCT: - case TYPE_ENUM: { - // @todo direct access - auto constant = (Ast_Decl *)type->ast; - auto name = constant->name; - sb->addf("%Q", name); - break; - } - case TYPE_VARGS: { - sb->addf("..."); - } break; - case TYPE_UNTYPED_BOOL: sb->addf("Untyped_Bool"); break; - case TYPE_UNTYPED_INT: sb->addf("Untyped_Int"); break; - case TYPE_UNTYPED_FLOAT: sb->addf("Untyped_Float"); break; - case TYPE_UNTYPED_STRING: sb->addf("Untyped_String"); break; - default: { - sb->addf("%s", get_name_of_type(type)); - } - } -} - -CORE_Static String -get_typename(Ast_Type *type) { - pctx->helper_builder.addf("["); - typename_base(&pctx->helper_builder, type); - pctx->helper_builder.addf("]"); - String result = string_flatten(pctx->stage_arena, &pctx->helper_builder); - pctx->helper_builder.reset(); - return result; -} - -CORE_Static String -typestring(Ast_Type *type) { - return get_typename(type); -} + +CORE_Static const char * +get_name_of_type(Ast_Type *type) { + switch (type->kind) { + case TYPE_VOID: return "void"; + case TYPE_BOOL: return "bool"; + case TYPE_F32: return "F32"; + case TYPE_F64: return "F64"; + case TYPE_S8: return "S8"; + + case TYPE_CHAR: return "char"; + case TYPE_UCHAR: return "uchar"; + case TYPE_INT: return "int"; + case TYPE_UINT: return "uint"; + case TYPE_LONG: return "long"; + case TYPE_ULONG: return "ulong"; + case TYPE_LLONG: return "llong"; + case TYPE_ULLONG: return "ullong"; + case TYPE_SHORT: return "short"; + case TYPE_USHORT: return "ushort"; + + case TYPE_S16: return "S16"; + case TYPE_S32: return "S32"; + case TYPE_S64: return "S64"; + case TYPE_U8: return "U8"; + case TYPE_U16: return "U16"; + case TYPE_U32: return "U32"; + case TYPE_U64: return "U64"; + case TYPE_TYPE: + return "Type"; + case TYPE_POLYMORPH: + return ""; + invalid_default_case; + } + return ""; +} + +//----------------------------------------------------------------------------- +// Type constructors and utillities +//----------------------------------------------------------------------------- +force_inline B32 is_any(Ast_Type *a) { return a == pctx->type_any; } +force_inline B32 is_struct(Ast_Type *a) { return a->kind == TYPE_STRUCT; } +force_inline B32 is_union(Ast_Type *a) { return a->kind == TYPE_UNION; } +force_inline B32 is_struct_union(Ast_Type *a) { return a->kind == TYPE_UNION || a->kind == TYPE_STRUCT; } +force_inline B32 is_lambda(Ast_Type *a) { return a->kind == TYPE_LAMBDA; } +force_inline B32 is_array(Ast_Type *a) { return a->kind == TYPE_ARRAY; } +force_inline B32 is_slice(Ast_Type *a) { return a->kind == TYPE_SLICE; } +force_inline B32 is_enum(Ast_Type *a) { return a->kind == TYPE_ENUM; } +force_inline B32 is_pointer(Ast_Type *a) { return a->kind == TYPE_POINTER; } +force_inline B32 is_void(Ast_Type *a) { return a->kind == TYPE_VOID; } +force_inline B32 is_void_pointer(Ast_Type *a) { return a == pctx->type_pointer_to_void; } +force_inline B32 is_string(Ast_Type *a) { return a == pctx->type_string || a->kind == TYPE_UNTYPED_STRING || a == pctx->type_pointer_to_char; } +force_inline B32 is_untyped_int(Ast_Type *a) { return a->kind == TYPE_UNTYPED_INT; } +force_inline B32 is_typed_int(Ast_Type *a) { return (a->kind >= TYPE_S64 && a->kind <= TYPE_U8); } +force_inline B32 is_int(Ast_Type *a) { return (a->kind >= TYPE_S64 && a->kind <= TYPE_U8) || a->kind == TYPE_UNTYPED_INT; } +force_inline B32 is_signed_int(Ast_Type *a) { return !a->is_unsigned; } +force_inline B32 is_unsigned_int(Ast_Type *a) { return a->is_unsigned; } +force_inline B32 is_float(Ast_Type *a) { return a->kind == TYPE_F32 || a->kind == TYPE_F64 || a->kind == TYPE_UNTYPED_FLOAT; } +force_inline B32 is_f32(Ast_Type *a) { return a->kind == TYPE_F32; } +force_inline B32 is_f64(Ast_Type *a) { return a->kind == TYPE_F64; } +force_inline B32 is_bool(Ast_Type *a) { return a->kind == TYPE_BOOL || a->kind == TYPE_UNTYPED_BOOL; } +force_inline B32 is_untyped(Ast_Type *a) { return a->kind >= TYPE_UNTYPED_FIRST && a->kind <= TYPE_UNTYPED_LAST; } +force_inline B32 is_typed(Ast_Type *a) { return !is_untyped(a); } +force_inline B32 is_numeric(Ast_Type *type) { + return (type->kind >= TYPE_UNTYPED_FIRST_NUMERIC && type->kind <= TYPE_UNTYPED_LAST_NUMERIC) || + (type->kind >= TYPE_FIRST_NUMERIC && type->kind <= TYPE_LAST_NUMERIC); +} + +//----------------------------------------------------------------------------- +// Hash consed types +//----------------------------------------------------------------------------- +CORE_Static Ast_Type * +type_new(Allocator *allocator, Ast_Type_Kind kind, int32_t size, int32_t align, bool generate_type_id = true) { + Ast_Type *result = allocate_struct(allocator, Ast_Type, true); + result->kind = kind; + result->size = size; + result->align = align; + if (generate_type_id) result->type_id = pctx->type_ids++; + add(pctx->perm, &pctx->all_types, result); + return result; +} + +CORE_Static Ast_Type * +type_copy(Allocator *a, Ast_Type *type) { + // @warning: This changes type id !!!! + Ast_Type *result = allocate_struct(a, Ast_Type); + memory_copy(result, type, sizeof(Ast_Type)); + result->type_id = pctx->type_ids++; + add(pctx->perm, &pctx->all_types, result); + return result; +} + +CORE_Static Ast_Type * +type_pointer(Ast_Type *base) { + Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, (void *)base); + if (!result) { + result = type_new(pctx->perm, TYPE_POINTER, pointer_size, pointer_align); + result->base = base; + result->is_unsigned = true; + map_insert(&pctx->type_map, base, result); + } + assert(result->kind == TYPE_POINTER); + return result; +} + +CORE_Static Ast_Type * +type_slice(Ast_Type *base, Ast *ast) { + U64 hash_base = hash_ptr(base); + U64 hash = hash_mix(hash_base, hash_u64(ARRAY_SIZE_SLICE)); + Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); + if (result) { + assert(result->kind == TYPE_SLICE); + assert(result->arr.base == base); + return result; + } + + struct Slice { + void *p; + S64 len; + }; + result = type_new(pctx->perm, TYPE_SLICE, sizeof(Slice), alignof(Slice)); + result->arr.base = base; + result->arr.slice_hash = hash; + result->ast = ast; + map_insert(&pctx->type_map, hash, result); + return result; +} + +CORE_Static Ast_Type * +type_array(Ast_Type *base, S32 size) { + U64 hash_base = hash_ptr(base); + U64 hash = hash_mix(hash_base, hash_u64(size)); + Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); + if (result) { + assert(result->kind == TYPE_ARRAY); + assert(result->arr.size == size); + assert(result->arr.base == base); + return result; + } + + result = type_new(pctx->perm, TYPE_ARRAY, size * base->size, pointer_align); + result->arr.base = base; + result->arr.size = size; + result->arr.slice_hash = hash_mix(hash_base, hash_u64(ARRAY_SIZE_SLICE)); + map_insert(&pctx->type_map, hash, result); + return result; +} + +inline U64 calculate_hash_for_arguments(Ast_Type *a, Ast_Type *b) { + U64 result = 13; + result = hash_mix(result, hash_ptr(a)); + result = hash_mix(result, hash_ptr(b)); + return result; +} + +inline U64 calculate_hash_for_arguments(Ast_Type *a) { + U64 result = 13; + result = hash_mix(result, hash_ptr(a)); + return result; +} + +CORE_Static Ast_Type * +type_lambda(Ast *ast, Ast_Type *ret, Array args) { + U64 hash_without_ret = 13; + For(args) hash_without_ret = hash_mix(hash_without_ret, hash_ptr(it)); + U64 hash = hash_mix(hash_ptr(ret), hash_without_ret); + Ast_Type *result = (Ast_Type *)map_get(&pctx->type_map, hash); + + if (result) { + assert(result->kind == TYPE_LAMBDA); + assert(result->func.args.len == args.len); + return result; + } + + result = type_new(pctx->perm, TYPE_LAMBDA, pointer_size, pointer_align); + result->ast = ast; + result->func.ret = ret; + result->func.args = args.tight_copy(pctx->perm); + result->func.hash_without_ret = hash_without_ret; + map_insert(&pctx->type_map, hash, result); + + return result; +} + +CORE_Static Ast_Type * +type_enum(Ast_Decl *ast, Ast_Type *type) { + if (!type) { + type = pctx->type_int; + } + + Ast_Type *result = type_new(pctx->perm, TYPE_ENUM, type->size, type->align); + result->base = type; + result->ast = ast; + return result; +} + +CORE_Static Ast_Type * +type_incomplete(Ast *ast) { + Ast_Type_Kind kind = TYPE_INCOMPLETE; + + bool generate_type_id = true; + if (is_flag_set(ast->flags, AST_POLYMORPH)) { + kind = TYPE_POLYMORPH; + generate_type_id = false; + } + + Ast_Type *result = type_new(pctx->perm, kind, 0, 0, generate_type_id); + result->ast = ast; + return result; +} + +CORE_Static void resolve_name_for_global_decl(Ast_Decl *node); +CORE_Static void type_complete(Ast_Type *type); +CORE_Static void +type_struct_complete(Ast_Type *type, Ast_Decl *node) { + assert(node->kind == AST_STRUCT || node->kind == AST_UNION); + if (is_flag_set(node->flags, AST_COMPILER_BREAKPOINT)) Breakpoint; + Scoped_Arena scratch(pctx->scratch); + + if (node->kind == AST_STRUCT) { + // First resolve and compute sizes of struct members + // + Array members = {scratch.arena}; + type->kind = TYPE_COMPLETING; + S32 members_size = 0; + For(node->scope->decls) { + resolve_decl(it); + assert(it->type->kind != TYPE_INCOMPLETE); + assert(is_pow2(it->type->align)); + + Ast_Resolved_Member m = {}; + m.offset = type->size; + members_size += it->type->size; + type->align = max(type->align, it->type->align); + type->size = it->type->size + (S32)align_up(type->size, it->type->align); + + m.name = it->name; + m.value = it->value; + members.add(m); + } + + // + // Then compute size of struct itself + // + type->size = (S32)align_up(type->size, type->align); + type->padding = type->size - members_size; + type->agg.members = members.tight_copy(pctx->perm); + type->kind = TYPE_STRUCT; + } + else { + assert(node->kind == AST_UNION); + // First resolve and compute sizes of union members + // + Array members = {scratch.arena}; + type->kind = TYPE_COMPLETING; + For(node->scope->decls) { + resolve_decl(it); + assert(it->type->kind != TYPE_INCOMPLETE); + assert(is_pow2(it->type->align)); + + type->align = max(type->align, it->type->align); + type->size = max(it->type->size, type->size); + + Ast_Resolved_Member m = {}; + m.name = it->name; + m.value = it->value; + members.add(m); + } + + // + // Then compute size of union itself + // + type->size = (S32)align_up(type->size, type->align); + type->agg.members = members.tight_copy(pctx->perm); + type->kind = TYPE_UNION; + } + resolve_name_for_global_decl(node); +} + +CORE_Static void +type_complete(Ast_Type *type) { + if (!type) { + return; + } + if (type->kind == TYPE_COMPLETING) { + compiler_error(type->ast->pos, "Cyclic type dependency"); + } + else if (type->kind != TYPE_INCOMPLETE) { + return; + } + + type_struct_complete(type, (Ast_Decl *)type->ast); + add(pctx->perm, &pctx->ordered_decls, (Ast_Decl *)type->ast); +} + +CORE_Static void +typename_base(String_Builder *sb, Ast_Type *type) { + switch (type->kind) { + case TYPE_INCOMPLETE: sb->addf("INCOMPLETE"); break; + case TYPE_COMPLETING: sb->addf("COMPLETING"); break; + case TYPE_TYPE: sb->addf("TYPE"); break; + case TYPE_POINTER: + sb->addf("*"); + typename_base(sb, type->base); + break; + case TYPE_LAMBDA: + sb->addf("("); + For(type->func.args) { + typename_base(sb, it); + if (!type->func.args.is_last(it)) sb->addf(", "); + } + + sb->addf("):"); + typename_base(sb, type->func.ret); + break; + case TYPE_ARRAY: + sb->addf("[%d]", (int)type->arr.size); + typename_base(sb, type->arr.base); + break; + case TYPE_SLICE: + sb->addf("[]"); + typename_base(sb, type->base); + break; + case TYPE_UNION: + case TYPE_STRUCT: + case TYPE_ENUM: { + // @todo direct access + auto constant = (Ast_Decl *)type->ast; + auto name = constant->name; + sb->addf("%Q", name); + break; + } + case TYPE_VARGS: { + sb->addf("..."); + } break; + case TYPE_UNTYPED_BOOL: sb->addf("Untyped_Bool"); break; + case TYPE_UNTYPED_INT: sb->addf("Untyped_Int"); break; + case TYPE_UNTYPED_FLOAT: sb->addf("Untyped_Float"); break; + case TYPE_UNTYPED_STRING: sb->addf("Untyped_String"); break; + default: { + sb->addf("%s", get_name_of_type(type)); + } + } +} + +CORE_Static String +get_typename(Ast_Type *type) { + pctx->helper_builder.addf("["); + typename_base(&pctx->helper_builder, type); + pctx->helper_builder.addf("]"); + String result = string_flatten(pctx->stage_arena, &pctx->helper_builder); + pctx->helper_builder.reset(); + return result; +} + +CORE_Static String +typestring(Ast_Type *type) { + return get_typename(type); +} diff --git a/core_types.h b/src/language/core_types.h similarity index 96% rename from core_types.h rename to src/language/core_types.h index b285f49..164b466 100644 --- a/core_types.h +++ b/src/language/core_types.h @@ -1,45 +1,45 @@ -//----------------------------------------------------------------------------- -// Resolved Types -//----------------------------------------------------------------------------- - -#define CASE_SINT \ - case TYPE_S8: \ - case TYPE_S16: \ - case TYPE_S32: \ - case TYPE_S64: \ - case TYPE_CHAR: \ - case TYPE_LONG: \ - case TYPE_LLONG: \ - case TYPE_SHORT: \ - case TYPE_INT -#define CASE_UINT \ - case TYPE_UINT: \ - case TYPE_ULONG: \ - case TYPE_ULLONG: \ - case TYPE_USHORT: \ - case TYPE_UCHAR: \ - case TYPE_U8: \ - case TYPE_U16: \ - case TYPE_U32: \ - case TYPE_U64 -#define CASE_INT \ - case TYPE_UNTYPED_INT: \ - CASE_SINT: \ - CASE_UINT -#define CASE_BOOL \ - case TYPE_UNTYPED_BOOL: \ - case TYPE_BOOL -#define CASE_FLOAT \ - case TYPE_UNTYPED_FLOAT: \ - case TYPE_F32: \ - case TYPE_F64 -#define CASE_STRING \ - case TYPE_UNTYPED_STRING: \ - case TYPE_STRUCT: \ - case TYPE_POINTER -#define CASE_UNTYPED \ - case TYPE_UNTYPED_INT: \ - case TYPE_UNTYPED_BOOL: \ - case TYPE_UNTYPED_FLOAT: \ - case TYPE_UNTYPED_STRING -#define ARRAY_SIZE_SLICE (-1) +//----------------------------------------------------------------------------- +// Resolved Types +//----------------------------------------------------------------------------- + +#define CASE_SINT \ + case TYPE_S8: \ + case TYPE_S16: \ + case TYPE_S32: \ + case TYPE_S64: \ + case TYPE_CHAR: \ + case TYPE_LONG: \ + case TYPE_LLONG: \ + case TYPE_SHORT: \ + case TYPE_INT +#define CASE_UINT \ + case TYPE_UINT: \ + case TYPE_ULONG: \ + case TYPE_ULLONG: \ + case TYPE_USHORT: \ + case TYPE_UCHAR: \ + case TYPE_U8: \ + case TYPE_U16: \ + case TYPE_U32: \ + case TYPE_U64 +#define CASE_INT \ + case TYPE_UNTYPED_INT: \ + CASE_SINT: \ + CASE_UINT +#define CASE_BOOL \ + case TYPE_UNTYPED_BOOL: \ + case TYPE_BOOL +#define CASE_FLOAT \ + case TYPE_UNTYPED_FLOAT: \ + case TYPE_F32: \ + case TYPE_F64 +#define CASE_STRING \ + case TYPE_UNTYPED_STRING: \ + case TYPE_STRUCT: \ + case TYPE_POINTER +#define CASE_UNTYPED \ + case TYPE_UNTYPED_INT: \ + case TYPE_UNTYPED_BOOL: \ + case TYPE_UNTYPED_FLOAT: \ + case TYPE_UNTYPED_STRING +#define ARRAY_SIZE_SLICE (-1) diff --git a/os.h b/src/os/os.h similarity index 94% rename from os.h rename to src/os/os.h index 42c288e..5000d8f 100644 --- a/os.h +++ b/src/os/os.h @@ -1,9 +1,9 @@ - -const U32 LIST_NO_FLAGS = 0; -const U32 LIST_RECURSE_INTO_DIRS = 1; - -struct OS_File_Info { - String relative_path; - String absolute_path; - B32 is_directory; -}; + +const U32 LIST_NO_FLAGS = 0; +const U32 LIST_RECURSE_INTO_DIRS = 1; + +struct OS_File_Info { + String relative_path; + String absolute_path; + B32 is_directory; +}; diff --git a/os_linux.cpp b/src/os/os_linux.cpp similarity index 96% rename from os_linux.cpp rename to src/os/os_linux.cpp index 80b94f6..17f0640 100644 --- a/os_linux.cpp +++ b/src/os/os_linux.cpp @@ -1,196 +1,196 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define POSIX_PAGE_SIZE 4096 - -CORE_Static B32 -os_write_file(String filename, String filecontent) { - FILE *f = fopen((const char *)filename.str, "w"); - if (f) { - fwrite(filecontent.str, 1, filecontent.len, f); - fclose(f); - return true; - } - return false; -} - -CORE_Static String -os_read_file(Alloator *a, String name) { - String result = {0}; - FILE *f = fopen((char *)name.str, "rb"); - if (f) { - fseek(f, 0, SEEK_END); - result.len = ftell(f); - fseek(f, 0, SEEK_SET); - result.str = (U8 *)allocate_size(a, result.len + 1, false); - fread(result.str, result.len, 1, f); - fclose(f); - result.str[result.len] = 0; - } - - return result; -} - -CORE_Static String -os_get_exe_dir(Allocator *a) { - char buffer[PATH_MAX] = {}; - if (readlink("/proc/self/exe", buffer, PATH_MAX) == -1) { - log_info("Failed to retrieve the path of the executable, the method used is fetching /proc/self/exe, very likely you are using an OS that doesn't follow this convention and as such it is currently not supported"); - } - String exe = string_from_cstring(buffer); - exe = string_chop_last_slash(exe); - String result = string_copy(a, exe); - - log_trace("Retrieved executable path %Q", result); - return result; -} - -CORE_Static String -os_get_absolute_path(Allocator *a, String path) { - assert(path.str[path.len] == 0); - char buffer[PATH_MAX] = {}; - realpath((char *)path.str, buffer); - String abs = string_from_cstring(buffer); - String result = string_copy(a, abs); - return result; -} - -CORE_Static B32 -os_does_file_exist(String path) { - B32 result = false; - assert(path.str[path.len] == 0); - if (access((char *)path.str, F_OK) == 0) { - result = true; - } - log_trace("Does file exist? %Q %d", path, result); - return result; -} - -CORE_Static String -os_get_working_dir(Allocator *allocator) { - char *buffer = allocate_array(allocator, char, PATH_MAX, false); - char *result = getcwd(buffer, PATH_MAX); - return string_from_cstring(result); -} - -CORE_Static Array -os_list_dir(Scratch_Arena *scratch, Allocator *a, String dir, U32 flags = LIST_RECURSE_INTO_DIRS) { - Scoped_Arena _scope(scratch); - Array dirs_to_read = {scratch}; - dirs_to_read.add(dir); - - Array result = {a}; - for (auto it = dirs_to_read.begin(); it != dirs_to_read.end(); it++) { - assert(it->str[it->len] == 0); - dirent *dir; - DIR *d = opendir((char *)it->str); - if (d) { - while ((dir = readdir(d)) != NULL) { - if (dir->d_name[0] == '.') { - if (dir->d_name[1] == '.') { - if (dir->d_name[2] == 0) - continue; - } - - if (dir->d_name[1] == 0) - continue; - } - - OS_File_Info entry = {}; - entry.relative_path = string_fmt(a, "%Q/%s", *it, dir->d_name); - entry.absolute_path = os_get_absolute_path(a, entry.relative_path); - - if (dir->d_type == DT_DIR) { - entry.is_directory = true; - - if (flags & LIST_RECURSE_INTO_DIRS) { - dirs_to_read.add(entry.absolute_path); - } - } - - result.add(entry); - log_trace("%Q %d", entry.absolute_path, entry.is_directory); - } - closedir(d); - } - else { - log_info("ERROR Failed to read dir: %Q, errno: %s", *it, strerror(errno)); - } - } - - return result; -} - -CORE_Static U8 * -os_advance_commit(OS_Memory *m, size_t *commit_size, size_t page_size) { - size_t aligned_up_commit = align_up(*commit_size, page_size); - size_t to_be_total_commited_size = aligned_up_commit + m->commit; - size_t to_be_total_commited_size_clamped_to_reserve = clamp_top(to_be_total_commited_size, m->reserve); - size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit; - assert_message(adjusted_to_boundary_commit, "Debug WIN32 Error: Reached the virtual memory reserved boundary"); - U8 *result = m->data + m->commit; - if (adjusted_to_boundary_commit == 0) - result = 0; - *commit_size = adjusted_to_boundary_commit; - return result; -} - -CORE_Static OS_Memory -os_reserve(size_t size) { - OS_Memory result = {}; - size_t size_aligned = align_up(size, POSIX_PAGE_SIZE); - result.data = (U8 *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - assert_message(result.data, POSIX_ERROR "Failed to reserve memory using mmap!!"); - if (result.data) { - result.reserve = size_aligned; - } - return result; -} - -CORE_Static B32 -os_commit(OS_Memory *m, size_t commit) { - B32 result = false; - U8 *pointer = os_advance_commit(m, &commit, POSIX_PAGE_SIZE); - if (pointer) { - int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE); - assert_message(mprotect_result == 0, "OS1 POSIX Debug Error: Failed to commit more memory using mmap"); - if (mprotect_result == 0) { - m->commit += commit; - result = true; - } - } - return result; -} - -CORE_Static void -os_release(OS_Memory *m) { - int result = munmap(m->data, m->reserve); - assert_message(result == 0, "OS1 POSIX Debug Error: Failed to release virtual memory using munmap"); - if (result == 0) { - memory_zero(m, sizeof(*m)); - } -} - -CORE_Static U64 -os_get_microseconds() { - timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - U64 result = (((U64)ts.tv_sec) * 1000000ull) + ((U64)ts.tv_nsec) / 1000ull; - return result; -} - -CORE_Static F64 -os_time() { - F64 time = (F64)os_get_microseconds(); - F64 result = time / 1000000.0; // Microseconds to seconds - return result; -} - +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define POSIX_PAGE_SIZE 4096 + +CORE_Static B32 +os_write_file(String filename, String filecontent) { + FILE *f = fopen((const char *)filename.str, "w"); + if (f) { + fwrite(filecontent.str, 1, filecontent.len, f); + fclose(f); + return true; + } + return false; +} + +CORE_Static String +os_read_file(Alloator *a, String name) { + String result = {0}; + FILE *f = fopen((char *)name.str, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + result.len = ftell(f); + fseek(f, 0, SEEK_SET); + result.str = (U8 *)allocate_size(a, result.len + 1, false); + fread(result.str, result.len, 1, f); + fclose(f); + result.str[result.len] = 0; + } + + return result; +} + +CORE_Static String +os_get_exe_dir(Allocator *a) { + char buffer[PATH_MAX] = {}; + if (readlink("/proc/self/exe", buffer, PATH_MAX) == -1) { + log_info("Failed to retrieve the path of the executable, the method used is fetching /proc/self/exe, very likely you are using an OS that doesn't follow this convention and as such it is currently not supported"); + } + String exe = string_from_cstring(buffer); + exe = string_chop_last_slash(exe); + String result = string_copy(a, exe); + + log_trace("Retrieved executable path %Q", result); + return result; +} + +CORE_Static String +os_get_absolute_path(Allocator *a, String path) { + assert(path.str[path.len] == 0); + char buffer[PATH_MAX] = {}; + realpath((char *)path.str, buffer); + String abs = string_from_cstring(buffer); + String result = string_copy(a, abs); + return result; +} + +CORE_Static B32 +os_does_file_exist(String path) { + B32 result = false; + assert(path.str[path.len] == 0); + if (access((char *)path.str, F_OK) == 0) { + result = true; + } + log_trace("Does file exist? %Q %d", path, result); + return result; +} + +CORE_Static String +os_get_working_dir(Allocator *allocator) { + char *buffer = allocate_array(allocator, char, PATH_MAX, false); + char *result = getcwd(buffer, PATH_MAX); + return string_from_cstring(result); +} + +CORE_Static Array +os_list_dir(Scratch_Arena *scratch, Allocator *a, String dir, U32 flags = LIST_RECURSE_INTO_DIRS) { + Scoped_Arena _scope(scratch); + Array dirs_to_read = {scratch}; + dirs_to_read.add(dir); + + Array result = {a}; + for (auto it = dirs_to_read.begin(); it != dirs_to_read.end(); it++) { + assert(it->str[it->len] == 0); + dirent *dir; + DIR *d = opendir((char *)it->str); + if (d) { + while ((dir = readdir(d)) != NULL) { + if (dir->d_name[0] == '.') { + if (dir->d_name[1] == '.') { + if (dir->d_name[2] == 0) + continue; + } + + if (dir->d_name[1] == 0) + continue; + } + + OS_File_Info entry = {}; + entry.relative_path = string_fmt(a, "%Q/%s", *it, dir->d_name); + entry.absolute_path = os_get_absolute_path(a, entry.relative_path); + + if (dir->d_type == DT_DIR) { + entry.is_directory = true; + + if (flags & LIST_RECURSE_INTO_DIRS) { + dirs_to_read.add(entry.absolute_path); + } + } + + result.add(entry); + log_trace("%Q %d", entry.absolute_path, entry.is_directory); + } + closedir(d); + } + else { + log_info("ERROR Failed to read dir: %Q, errno: %s", *it, strerror(errno)); + } + } + + return result; +} + +CORE_Static U8 * +os_advance_commit(OS_Memory *m, size_t *commit_size, size_t page_size) { + size_t aligned_up_commit = align_up(*commit_size, page_size); + size_t to_be_total_commited_size = aligned_up_commit + m->commit; + size_t to_be_total_commited_size_clamped_to_reserve = clamp_top(to_be_total_commited_size, m->reserve); + size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit; + assert_message(adjusted_to_boundary_commit, "Debug WIN32 Error: Reached the virtual memory reserved boundary"); + U8 *result = m->data + m->commit; + if (adjusted_to_boundary_commit == 0) + result = 0; + *commit_size = adjusted_to_boundary_commit; + return result; +} + +CORE_Static OS_Memory +os_reserve(size_t size) { + OS_Memory result = {}; + size_t size_aligned = align_up(size, POSIX_PAGE_SIZE); + result.data = (U8 *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert_message(result.data, POSIX_ERROR "Failed to reserve memory using mmap!!"); + if (result.data) { + result.reserve = size_aligned; + } + return result; +} + +CORE_Static B32 +os_commit(OS_Memory *m, size_t commit) { + B32 result = false; + U8 *pointer = os_advance_commit(m, &commit, POSIX_PAGE_SIZE); + if (pointer) { + int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE); + assert_message(mprotect_result == 0, "OS1 POSIX Debug Error: Failed to commit more memory using mmap"); + if (mprotect_result == 0) { + m->commit += commit; + result = true; + } + } + return result; +} + +CORE_Static void +os_release(OS_Memory *m) { + int result = munmap(m->data, m->reserve); + assert_message(result == 0, "OS1 POSIX Debug Error: Failed to release virtual memory using munmap"); + if (result == 0) { + memory_zero(m, sizeof(*m)); + } +} + +CORE_Static U64 +os_get_microseconds() { + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + U64 result = (((U64)ts.tv_sec) * 1000000ull) + ((U64)ts.tv_nsec) / 1000ull; + return result; +} + +CORE_Static F64 +os_time() { + F64 time = (F64)os_get_microseconds(); + F64 result = time / 1000000.0; // Microseconds to seconds + return result; +} + bool os_enable_console_colors() { return true; } \ No newline at end of file diff --git a/os_windows.cpp b/src/os/os_windows.cpp similarity index 96% rename from os_windows.cpp rename to src/os/os_windows.cpp index b987cf1..e59dab3 100644 --- a/os_windows.cpp +++ b/src/os/os_windows.cpp @@ -1,242 +1,242 @@ -#include - -//----------------------------------------------------------------------------- -// Memory -//----------------------------------------------------------------------------- -const size_t os_page_size = 4096; - -CORE_Static OS_Memory -os_reserve(size_t size) { - OS_Memory result = {}; - size_t adjusted_size = align_up(size, os_page_size); - result.data = (U8 *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE); - assert_message(result.data, "Failed to reserve virtual memory"); - result.reserve = adjusted_size; - return result; -} - -CORE_Static B32 -os_commit(OS_Memory *m, size_t size) { - size_t commit = align_up(size, os_page_size); - size_t total_commit = m->commit + commit; - total_commit = clamp_top(total_commit, m->reserve); - size_t adjusted_commit = total_commit - m->commit; - if (adjusted_commit != 0) { - void *result = VirtualAlloc((U8 *)m->data + m->commit, adjusted_commit, MEM_COMMIT, PAGE_READWRITE); - assert_message(result, "Failed to commit more memory"); - m->commit += adjusted_commit; - return true; - } - return false; -} - -CORE_Static void -os_release(OS_Memory *m) { - BOOL result = VirtualFree(m->data, 0, MEM_RELEASE); - assert_message(result != 0, "Failed to release OS_Memory"); - if (result) { - m->data = 0; - m->commit = 0; - m->reserve = 0; - } -} - -CORE_Static B32 -os_decommit_pos(OS_Memory *m, size_t pos) { - size_t aligned = align_down(pos, os_page_size); - size_t adjusted_pos = clamp_top(aligned, m->commit); - size_t size_to_decommit = m->commit - adjusted_pos; - if (size_to_decommit) { - U8 *base_address = m->data + adjusted_pos; - BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT); - if (result) { - m->commit -= size_to_decommit; - return true; - } - } - return false; -} - -//----------------------------------------------------------------------------- -// Time -//----------------------------------------------------------------------------- -global S64 Global_counts_per_second; -api F64 os_time() { - if (Global_counts_per_second == 0) { - LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - Global_counts_per_second = freq.QuadPart; - } - - LARGE_INTEGER time; - QueryPerformanceCounter(&time); - F64 result = (F64)time.QuadPart / (F64)Global_counts_per_second; - return result; -} - -//----------------------------------------------------------------------------- -// Filesystem -//----------------------------------------------------------------------------- -CORE_Static B32 -os_write_file(String filename, String filecontent) { - FILE *f = fopen((const char *)filename.str, "w"); - if (f) { - fwrite(filecontent.str, 1, filecontent.len, f); - fclose(f); - return true; - } - return false; -} - -CORE_Static String -os_read_file(Allocator *a, String name) { - String result = {0}; - FILE *f = fopen((char *)name.str, "rb"); - if (f) { - fseek(f, 0, SEEK_END); - result.len = ftell(f); - fseek(f, 0, SEEK_SET); - result.str = (U8 *)allocate_size(a, result.len + 1, false); - fread(result.str, result.len, 1, f); - fclose(f); - result.str[result.len] = 0; - } - - return result; -} - -CORE_Static String -os_get_working_dir(Allocator *a) { - wchar_t buffer[2048]; - DWORD written = GetCurrentDirectoryW(2048, buffer); - assert(written != 0); - String16 string16 = string16_from_widechar(buffer); - String result = string16_to_string8(a, string16); - string_path_normalize(result); - return result; -} - -CORE_Static String -os_get_exe_dir(Allocator *a) { - wchar_t buffer[2048]; - DWORD written = GetModuleFileNameW(0, buffer, 2048); - assert(written != 0); - String16 string16 = string16_from_widechar(buffer); - String result = string16_to_string8(a, string16); - string_path_normalize(result); - result = string_chop_last_slash(result); - - if (string16.len > result.len) result.str[result.len] = 0; - string_path_normalize(result); - return result; -} - -CORE_Static String -os_get_absolute_path(Allocator *a, String path) { - char buff[2048]; - Arena scratch = arena_from_buffer(buff, 2048); - String16 path16 = string8_to_string16(&scratch, path); - - wchar_t *buffer = allocate_array(&scratch, wchar_t, 512); - DWORD written = GetFullPathNameW((wchar_t *)path16.str, 512, buffer, 0); - if (written == 0) return {}; - - String16 absolute16 = string16_from_widechar(buffer); - String absolute = string16_to_string8(a, absolute16); - string_path_normalize(absolute); - return absolute; -} - -CORE_Static B32 -os_does_file_exist(String path) { - char buff[2048]; - Arena scratch = arena_from_buffer(buff, buff_cap(buff)); - String16 path16 = string8_to_string16(&scratch, path); - DWORD attribs = GetFileAttributesW((wchar_t *)path16.str); - B32 result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; - return result; -} - -CORE_Static Array -os_list_dir(Arena *scratch, Allocator *a, String dir, U32 flags = LIST_NO_FLAGS) { - Scoped_Arena _scope(scratch); - Array dirs_to_read = {scratch}; - dirs_to_read.add(dir); - - Array result = {a}; - for (auto it = dirs_to_read.begin(); it != dirs_to_read.end(); it++) { - String modified_path = string_fmt(scratch, "%Q\\*", it); - String16 path16 = string8_to_string16(scratch, modified_path); - - WIN32_FIND_DATAW ffd; - HANDLE handle = FindFirstFileW((wchar_t *)path16.str, &ffd); - if (handle == INVALID_HANDLE_VALUE) continue; - - do { - - // - // Skip '.' and '..' - // - if (ffd.cFileName[0] == '.') { - if (ffd.cFileName[1] == '.') { - if (ffd.cFileName[2] == 0) - continue; - } - - if (ffd.cFileName[1] == 0) - continue; - } - - String16 filename16 = string16_from_widechar(ffd.cFileName); - String filename = string16_to_string8(scratch, filename16); - - String full_file_path = string_fmt(a, "%Q/%Q", dir, filename); - OS_File_Info listing = {full_file_path, os_get_absolute_path(a, full_file_path)}; - - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - listing.is_directory = true; - - if (flags & LIST_RECURSE_INTO_DIRS) { - dirs_to_read.add(full_file_path); - } - } - - result.add(listing); - - } while (FindNextFileW(handle, &ffd) != 0); - - DWORD error = GetLastError(); - if (error != ERROR_NO_MORE_FILES) { - // Not sure what to do here hmmm - } - FindClose(handle); - } - - return result; -} - -bool GLOBAL_EnabledConsoleColors; -bool os_enable_console_colors() { - // Set output mode to handle virtual terminal sequences - HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); - if (hOut != INVALID_HANDLE_VALUE) { - DWORD dwMode = 0; - if (GetConsoleMode(hOut, &dwMode)) { - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if (SetConsoleMode(hOut, dwMode)) { - GLOBAL_EnabledConsoleColors = true; - return true; - } - else { - printf("Failed to enable colored terminal output C\n"); - } - } - else { - printf("Failed to enable colored terminal output B\n"); - } - } - else { - printf("Failed to enable colored terminal output A\n"); - } - return false; +#include + +//----------------------------------------------------------------------------- +// Memory +//----------------------------------------------------------------------------- +const size_t os_page_size = 4096; + +CORE_Static OS_Memory +os_reserve(size_t size) { + OS_Memory result = {}; + size_t adjusted_size = align_up(size, os_page_size); + result.data = (U8 *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE); + assert_message(result.data, "Failed to reserve virtual memory"); + result.reserve = adjusted_size; + return result; +} + +CORE_Static B32 +os_commit(OS_Memory *m, size_t size) { + size_t commit = align_up(size, os_page_size); + size_t total_commit = m->commit + commit; + total_commit = clamp_top(total_commit, m->reserve); + size_t adjusted_commit = total_commit - m->commit; + if (adjusted_commit != 0) { + void *result = VirtualAlloc((U8 *)m->data + m->commit, adjusted_commit, MEM_COMMIT, PAGE_READWRITE); + assert_message(result, "Failed to commit more memory"); + m->commit += adjusted_commit; + return true; + } + return false; +} + +CORE_Static void +os_release(OS_Memory *m) { + BOOL result = VirtualFree(m->data, 0, MEM_RELEASE); + assert_message(result != 0, "Failed to release OS_Memory"); + if (result) { + m->data = 0; + m->commit = 0; + m->reserve = 0; + } +} + +CORE_Static B32 +os_decommit_pos(OS_Memory *m, size_t pos) { + size_t aligned = align_down(pos, os_page_size); + size_t adjusted_pos = clamp_top(aligned, m->commit); + size_t size_to_decommit = m->commit - adjusted_pos; + if (size_to_decommit) { + U8 *base_address = m->data + adjusted_pos; + BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT); + if (result) { + m->commit -= size_to_decommit; + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +// Time +//----------------------------------------------------------------------------- +global S64 Global_counts_per_second; +api F64 os_time() { + if (Global_counts_per_second == 0) { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + Global_counts_per_second = freq.QuadPart; + } + + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + F64 result = (F64)time.QuadPart / (F64)Global_counts_per_second; + return result; +} + +//----------------------------------------------------------------------------- +// Filesystem +//----------------------------------------------------------------------------- +CORE_Static B32 +os_write_file(String filename, String filecontent) { + FILE *f = fopen((const char *)filename.str, "w"); + if (f) { + fwrite(filecontent.str, 1, filecontent.len, f); + fclose(f); + return true; + } + return false; +} + +CORE_Static String +os_read_file(Allocator *a, String name) { + String result = {0}; + FILE *f = fopen((char *)name.str, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + result.len = ftell(f); + fseek(f, 0, SEEK_SET); + result.str = (U8 *)allocate_size(a, result.len + 1, false); + fread(result.str, result.len, 1, f); + fclose(f); + result.str[result.len] = 0; + } + + return result; +} + +CORE_Static String +os_get_working_dir(Allocator *a) { + wchar_t buffer[2048]; + DWORD written = GetCurrentDirectoryW(2048, buffer); + assert(written != 0); + String16 string16 = string16_from_widechar(buffer); + String result = string16_to_string8(a, string16); + string_path_normalize(result); + return result; +} + +CORE_Static String +os_get_exe_dir(Allocator *a) { + wchar_t buffer[2048]; + DWORD written = GetModuleFileNameW(0, buffer, 2048); + assert(written != 0); + String16 string16 = string16_from_widechar(buffer); + String result = string16_to_string8(a, string16); + string_path_normalize(result); + result = string_chop_last_slash(result); + + if (string16.len > result.len) result.str[result.len] = 0; + string_path_normalize(result); + return result; +} + +CORE_Static String +os_get_absolute_path(Allocator *a, String path) { + char buff[2048]; + Arena scratch = arena_from_buffer(buff, 2048); + String16 path16 = string8_to_string16(&scratch, path); + + wchar_t *buffer = allocate_array(&scratch, wchar_t, 512); + DWORD written = GetFullPathNameW((wchar_t *)path16.str, 512, buffer, 0); + if (written == 0) return {}; + + String16 absolute16 = string16_from_widechar(buffer); + String absolute = string16_to_string8(a, absolute16); + string_path_normalize(absolute); + return absolute; +} + +CORE_Static B32 +os_does_file_exist(String path) { + char buff[2048]; + Arena scratch = arena_from_buffer(buff, buff_cap(buff)); + String16 path16 = string8_to_string16(&scratch, path); + DWORD attribs = GetFileAttributesW((wchar_t *)path16.str); + B32 result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; + return result; +} + +CORE_Static Array +os_list_dir(Arena *scratch, Allocator *a, String dir, U32 flags = LIST_NO_FLAGS) { + Scoped_Arena _scope(scratch); + Array dirs_to_read = {scratch}; + dirs_to_read.add(dir); + + Array result = {a}; + for (auto it = dirs_to_read.begin(); it != dirs_to_read.end(); it++) { + String modified_path = string_fmt(scratch, "%Q\\*", it); + String16 path16 = string8_to_string16(scratch, modified_path); + + WIN32_FIND_DATAW ffd; + HANDLE handle = FindFirstFileW((wchar_t *)path16.str, &ffd); + if (handle == INVALID_HANDLE_VALUE) continue; + + do { + + // + // Skip '.' and '..' + // + if (ffd.cFileName[0] == '.') { + if (ffd.cFileName[1] == '.') { + if (ffd.cFileName[2] == 0) + continue; + } + + if (ffd.cFileName[1] == 0) + continue; + } + + String16 filename16 = string16_from_widechar(ffd.cFileName); + String filename = string16_to_string8(scratch, filename16); + + String full_file_path = string_fmt(a, "%Q/%Q", dir, filename); + OS_File_Info listing = {full_file_path, os_get_absolute_path(a, full_file_path)}; + + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + listing.is_directory = true; + + if (flags & LIST_RECURSE_INTO_DIRS) { + dirs_to_read.add(full_file_path); + } + } + + result.add(listing); + + } while (FindNextFileW(handle, &ffd) != 0); + + DWORD error = GetLastError(); + if (error != ERROR_NO_MORE_FILES) { + // Not sure what to do here hmmm + } + FindClose(handle); + } + + return result; +} + +bool GLOBAL_EnabledConsoleColors; +bool os_enable_console_colors() { + // Set output mode to handle virtual terminal sequences + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut != INVALID_HANDLE_VALUE) { + DWORD dwMode = 0; + if (GetConsoleMode(hOut, &dwMode)) { + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (SetConsoleMode(hOut, dwMode)) { + GLOBAL_EnabledConsoleColors = true; + return true; + } + else { + printf("Failed to enable colored terminal output C\n"); + } + } + else { + printf("Failed to enable colored terminal output B\n"); + } + } + else { + printf("Failed to enable colored terminal output A\n"); + } + return false; } \ No newline at end of file diff --git a/meta.bat b/tools/meta.bat similarity index 96% rename from meta.bat rename to tools/meta.bat index 0fa8fb3..333b14a 100644 --- a/meta.bat +++ b/tools/meta.bat @@ -1,4 +1,4 @@ -@echo off - -call meta_run.bat preprocess cpp +@echo off + +call meta_run.bat preprocess cpp clang-format -i *.cpp *.hpp *.h \ No newline at end of file diff --git a/meta.py b/tools/meta.py similarity index 95% rename from meta.py rename to tools/meta.py index 5b878a9..4e126a0 100644 --- a/meta.py +++ b/tools/meta.py @@ -1,159 +1,159 @@ -import re -snake_case_pattern = re.compile(r'(?>", BINARY_EXPR], - ["FirstMul = TK_Mul", "SPECIAL"], - ["LastMul = TK_RightShift", "SPECIAL"], - ["Add", "+", BINARY_EXPR | UNARY_EXPR], - ["Sub", "-", BINARY_EXPR | UNARY_EXPR], - ["FirstAdd = TK_Add", "SPECIAL"], - ["LastAdd = TK_Sub", "SPECIAL"], - ["Equals", "==", BINARY_EXPR], - ["LesserThenOrEqual", "<=", BINARY_EXPR], - ["GreaterThenOrEqual", ">=", BINARY_EXPR], - ["LesserThen", "<", BINARY_EXPR], - ["GreaterThen", ">", BINARY_EXPR], - ["NotEquals", "!=", BINARY_EXPR], - ["FirstCompare = TK_Equals", "SPECIAL"], - ["LastCompare = TK_NotEquals", "SPECIAL"], - ["BitAnd", "&", BINARY_EXPR], - ["BitOr", "|", BINARY_EXPR], - ["BitXor", "^", BINARY_EXPR], - ["And", "&&", BINARY_EXPR], - ["Or", "||", BINARY_EXPR], - ["FirstLogical = TK_BitAnd", "SPECIAL"], - ["LastLogical = TK_Or", "SPECIAL"], - - ["Neg", "~", UNARY_EXPR], - ["Not", "!", UNARY_EXPR], -] - -token_inc_expr = [ - ["Decrement", "--", UNARY_EXPR], - ["Increment", "++", UNARY_EXPR], - ["PostDecrement", "--", UNARY_EXPR], - ["PostIncrement", "++", UNARY_EXPR], -] - -token_assign_expr = [ - ["Assign", "="], - ["ColonAssign", ":="], - ["DivAssign", "/="], - ["MulAssign", "*="], - ["ModAssign", "%="], - ["SubAssign", "-="], - ["AddAssign", "+="], - ["AndAssign", "&="], - ["OrAssign", "|="], - ["XorAssign", "^="], - ["LeftShiftAssign", "<<="], - ["RightShiftAssign", ">>="], - ["FirstAssign = TK_Assign", "SPECIAL"], - ["LastAssign = TK_RightShiftAssign", "SPECIAL"], -] - -token_rest = [ - ["OpenParen", "("], - ["CloseParen", ")"], - ["OpenBrace", "{"], - ["CloseBrace", "}"], - ["OpenBracket", "["], - ["CloseBracket", "]"], - ["Comma", ","], - ["Pound", "#"], - ["Question", "?"], - ["ThreeDots", "..."], - ["Semicolon", ";"], - ["Dot", "."], - ["TwoDots", ".."], - ["NewLine", "[NewLine]"], - ["Colon", ":"], - ["DoubleColon", "::"], - ["At", "@"], - ["Arrow", "->"], - ["Polymorph", "$"], - ["ExprSizeof", "[sizeof]"], - ["DocComment", "[///]"], - ["Comment", "//"], - ["Identifier", "[Ident]"], - ["UnicodeLit", "[Unicode]"], - ["StringLit", "[String]"], - ["Error", "[Error]"], - ["Float", "[Float]"], - ["Integer", "[Int]"], - ["Keyword", "[Keyword]"], -] - -token_kinds = token_simple_expr + token_inc_expr + token_assign_expr + token_rest - -keywords = [ - "struct", - "union", - "true", - "default", - "continue", - "break", - "false", - "return", - "switch", - "Assert", - "if", - "elif", - "pass", - "else", - "for", - "enum", - "goto", - "defer", -] - -interns = [ - "typeof", - "sizeof", - "Len", - "alignof", - "foreign", - "strict", - "void", - "flag", - "it", - "load", - "import", - "link", - "compiler_breakpoint", -] - - -value_struct_content = """ -Ast_Type *type; -Ast_Decl *resolved_decl; -union { - bool bool_val; - double f64_val; - Intern_String intern_val; - BigInt big_int_val; - Ast_Type *type_val; -}; -""".strip() - -def inline_value_fields(): - print(f""" -union {{ - Value value; - struct {{ - {value_struct_content} - }}; -}}; - """.strip()) +import re +snake_case_pattern = re.compile(r'(?>", BINARY_EXPR], + ["FirstMul = TK_Mul", "SPECIAL"], + ["LastMul = TK_RightShift", "SPECIAL"], + ["Add", "+", BINARY_EXPR | UNARY_EXPR], + ["Sub", "-", BINARY_EXPR | UNARY_EXPR], + ["FirstAdd = TK_Add", "SPECIAL"], + ["LastAdd = TK_Sub", "SPECIAL"], + ["Equals", "==", BINARY_EXPR], + ["LesserThenOrEqual", "<=", BINARY_EXPR], + ["GreaterThenOrEqual", ">=", BINARY_EXPR], + ["LesserThen", "<", BINARY_EXPR], + ["GreaterThen", ">", BINARY_EXPR], + ["NotEquals", "!=", BINARY_EXPR], + ["FirstCompare = TK_Equals", "SPECIAL"], + ["LastCompare = TK_NotEquals", "SPECIAL"], + ["BitAnd", "&", BINARY_EXPR], + ["BitOr", "|", BINARY_EXPR], + ["BitXor", "^", BINARY_EXPR], + ["And", "&&", BINARY_EXPR], + ["Or", "||", BINARY_EXPR], + ["FirstLogical = TK_BitAnd", "SPECIAL"], + ["LastLogical = TK_Or", "SPECIAL"], + + ["Neg", "~", UNARY_EXPR], + ["Not", "!", UNARY_EXPR], +] + +token_inc_expr = [ + ["Decrement", "--", UNARY_EXPR], + ["Increment", "++", UNARY_EXPR], + ["PostDecrement", "--", UNARY_EXPR], + ["PostIncrement", "++", UNARY_EXPR], +] + +token_assign_expr = [ + ["Assign", "="], + ["ColonAssign", ":="], + ["DivAssign", "/="], + ["MulAssign", "*="], + ["ModAssign", "%="], + ["SubAssign", "-="], + ["AddAssign", "+="], + ["AndAssign", "&="], + ["OrAssign", "|="], + ["XorAssign", "^="], + ["LeftShiftAssign", "<<="], + ["RightShiftAssign", ">>="], + ["FirstAssign = TK_Assign", "SPECIAL"], + ["LastAssign = TK_RightShiftAssign", "SPECIAL"], +] + +token_rest = [ + ["OpenParen", "("], + ["CloseParen", ")"], + ["OpenBrace", "{"], + ["CloseBrace", "}"], + ["OpenBracket", "["], + ["CloseBracket", "]"], + ["Comma", ","], + ["Pound", "#"], + ["Question", "?"], + ["ThreeDots", "..."], + ["Semicolon", ";"], + ["Dot", "."], + ["TwoDots", ".."], + ["NewLine", "[NewLine]"], + ["Colon", ":"], + ["DoubleColon", "::"], + ["At", "@"], + ["Arrow", "->"], + ["Polymorph", "$"], + ["ExprSizeof", "[sizeof]"], + ["DocComment", "[///]"], + ["Comment", "//"], + ["Identifier", "[Ident]"], + ["UnicodeLit", "[Unicode]"], + ["StringLit", "[String]"], + ["Error", "[Error]"], + ["Float", "[Float]"], + ["Integer", "[Int]"], + ["Keyword", "[Keyword]"], +] + +token_kinds = token_simple_expr + token_inc_expr + token_assign_expr + token_rest + +keywords = [ + "struct", + "union", + "true", + "default", + "continue", + "break", + "false", + "return", + "switch", + "Assert", + "if", + "elif", + "pass", + "else", + "for", + "enum", + "goto", + "defer", +] + +interns = [ + "typeof", + "sizeof", + "Len", + "alignof", + "foreign", + "strict", + "void", + "flag", + "it", + "load", + "import", + "link", + "compiler_breakpoint", +] + + +value_struct_content = """ +Ast_Type *type; +Ast_Decl *resolved_decl; +union { + bool bool_val; + double f64_val; + Intern_String intern_val; + BigInt big_int_val; + Ast_Type *type_val; +}; +""".strip() + +def inline_value_fields(): + print(f""" +union {{ + Value value; + struct {{ + {value_struct_content} + }}; +}}; + """.strip()) diff --git a/meta_run.py b/tools/meta_run.py similarity index 96% rename from meta_run.py rename to tools/meta_run.py index 92bea22..5e51e6f 100644 --- a/meta_run.py +++ b/tools/meta_run.py @@ -1,65 +1,65 @@ -import subprocess -import sys -import os - -files = os.listdir(".") -files = [i for i in files if (i.endswith(".cpp") or i.endswith(".h")) and i != "test.cpp"] - -for file_to_modify in files: - print(file_to_modify) - fd = open(file_to_modify, "r+") - f = fd.read() - END = "/*END*/" - START = "/*#" - STOP = "*/" - - program_counter = 0 - valid = True - index = 0 - while True: - begin = f.find(START, index) - if begin == -1: - break - index = begin - begin += 3 - - end = f.find(STOP, index) - if end == -1: - valid = False - print(f"One of the registered comments inside {file_to_modify} doesn't have an end") - break - - gen_end = f.find(END, end) - next_block = f.find(START, end) - if next_block != -1 and gen_end > next_block: - gen_end = -1 - - before = f[:begin] - program = f[begin:end] - if gen_end == -1: - after = f[end+2:] - else: - after = f[gen_end+len(END):] - - temp_filename = "_metaprogram" + str(program_counter) + ".py" - if os.path.isfile(temp_filename): - print(temp_filename + " exists, dont want to risk overwriting something important") - valid = False - break - program_counter += 1 - with open(temp_filename, "w") as meta_file: - meta_file.write(program) - - result = subprocess.run([sys.executable, temp_filename], stdout=subprocess.PIPE) - program_result = result.stdout.decode('utf-8').replace('\r\n', '\n') + END - f = before + program + "*/\n" + program_result + after - os.remove(temp_filename) - - index = end - - if valid: - fd.seek(0) - fd.write(f) - fd.truncate() - fd.close() - +import subprocess +import sys +import os + +files = os.listdir(".") +files = [i for i in files if (i.endswith(".cpp") or i.endswith(".h")) and i != "test.cpp"] + +for file_to_modify in files: + print(file_to_modify) + fd = open(file_to_modify, "r+") + f = fd.read() + END = "/*END*/" + START = "/*#" + STOP = "*/" + + program_counter = 0 + valid = True + index = 0 + while True: + begin = f.find(START, index) + if begin == -1: + break + index = begin + begin += 3 + + end = f.find(STOP, index) + if end == -1: + valid = False + print(f"One of the registered comments inside {file_to_modify} doesn't have an end") + break + + gen_end = f.find(END, end) + next_block = f.find(START, end) + if next_block != -1 and gen_end > next_block: + gen_end = -1 + + before = f[:begin] + program = f[begin:end] + if gen_end == -1: + after = f[end+2:] + else: + after = f[gen_end+len(END):] + + temp_filename = "_metaprogram" + str(program_counter) + ".py" + if os.path.isfile(temp_filename): + print(temp_filename + " exists, dont want to risk overwriting something important") + valid = False + break + program_counter += 1 + with open(temp_filename, "w") as meta_file: + meta_file.write(program) + + result = subprocess.run([sys.executable, temp_filename], stdout=subprocess.PIPE) + program_result = result.stdout.decode('utf-8').replace('\r\n', '\n') + END + f = before + program + "*/\n" + program_result + after + os.remove(temp_filename) + + index = end + + if valid: + fd.seek(0) + fd.write(f) + fd.truncate() + fd.close() +