Compare commits

...
Author SHA1 Message Date
Patedam 5fa75c116e blue window 2026-10-27 23:06:45 -04:00
Patedam 2881b2c9d5 started d3d12 renderer. device + swap chain + associate to window 2026-10-26 22:52:08 -04:00
Patedam d79cf42a3d starting render/d3d12 layer 2026-10-25 23:37:14 -04:00
Patedam 17af5348e9 more keyboard function (is down) 2026-10-25 20:49:27 -04:00
Patedam dec8cbd059 keyboard support improved. F1 open a new window. 2026-10-25 18:12:51 -04:00
Patedam f580776964 can close windows independently 2026-10-25 15:57:52 -04:00
Patedam d2d9981efe two windows open + can close the app by pressing X 2026-10-25 15:23:07 -04:00
Patedam f6b48a1c11 window management. still missing first paint but progressing 2026-10-24 22:52:26 -04:00
Patedam bf13f269fe almost there (open window) 2026-10-24 18:17:43 -04:00
Patedam cf86dd10a7 lock submodules to version + move from stb to stb_sprintf 2026-10-24 17:36:47 -04:00
Patedam 5995dd9a47 Replace stb with standalone stb_sprintf and pin submodule versions 2026-10-24 17:31:55 -04:00
Patedam d5c6c79a76 adding xxhash 2026-10-24 17:16:41 -04:00
Patedam 7e8e626e24 adding hash library + multi windows support being started 2026-10-24 17:05:55 -04:00
Patedam f96b2e99cf windows open now 2026-10-23 23:35:45 -04:00
Patedam 4ec42f4bee open window code still not opening :) 2026-10-22 22:58:32 -04:00
Patedam 886e722c71 started window management 2026-10-20 23:15:56 -04:00
Patedam 575379e264 added thread naming 2026-10-18 23:41:43 -04:00
Patedam 17e26fdd00 fix build error 2026-10-18 23:07:59 -04:00
Patedam 5d6ca639fd Adding more core layer stuff. Can now boot. 2026-10-18 23:06:41 -04:00
Patedam 16664c68b5 wok on arena 2026-10-18 00:25:02 -04:00
Patedam ee1878cce7 core layer 2026-10-17 23:21:16 -04:00
Patedam 7542d9200d Merge pull request 'conversion to c by gemini 4 argon' (#3) from convert-to-c99 into main
Reviewed-on: #3
2026-10-17 17:19:10 -04:00
Patedam f9356f9ba7 conversion to c by gemini 4 argon 2026-10-16 23:40:45 -04:00
Patedam b70e2d21d2 entity per world 2026-10-16 21:33:57 -04:00
Patedam 16ba641d8a Support of clangd for the project 2026-10-05 23:37:18 -04:00
Patedam 7075556bf2 Reading / writing user id based on os username 2026-09-19 21:48:02 -04:00
Patedam f32a3f73a9 more path utils and fixing get os username that was returning a string including the \0
User now can be serialized properly
2026-09-18 21:19:38 -04:00
Patedam 1367146503 natvis support in build system + some natvis 2026-09-18 18:23:49 -04:00
Patedam 2fd675cf11 Added utils and types to handle paths 2026-09-18 18:14:00 -04:00
Patedam f77b7fb901 clean all path returned by GetAssetPath 2026-09-16 20:43:48 -04:00
Patedam 10ac51f167 Merge branch 'main' of https://gitea.itspm.cc/Patedam/Juliet 2026-09-16 18:42:06 -04:00
Patedam 9e8bbad5d4 Working on supporting multiple users during edition. For now prepare a way to write a .ini file with user and its id. 2026-09-16 18:41:50 -04:00
Patedam 0f262fb06e Working on supporting multiple users during edition. For now prepare a way to write a .ini file with user and its id. 2026-09-16 18:41:37 -04:00
Patedam c70cce463b Modifying GetAssetPath to support more than just shaders (added jasset) 2026-09-15 19:31:34 -04:00
Patedam 2bd0cf39f1 serialization fixing world unit test and missing serialize for inert. 2026-09-13 11:59:10 -04:00
Patedam 58b79fb499 wip entity deserialization phase 1. Unit test not passing because of globals that assume only one entity manager / game state. 2026-09-10 22:50:18 -04:00
Patedam 615d36b09c Serialization : started to unify how to allocate entity whether its from MakeEntity or during deserialization. 2026-09-08 23:24:48 -04:00
Patedam 3123d2f2e6 updating serialization tdd with latest changes 2026-09-07 20:05:17 -04:00
Patedam b1de6ccc49 finishing first step of serialization : the basics 2026-09-07 19:42:15 -04:00
Patedam a462575af4 ongoing refactor of serialization and various cleanup 2026-09-07 16:58:57 -04:00
Patedam 1a045c3578 updating coding guidelines + plan for full juliet serialization and entity system 2026-09-06 12:08:57 -04:00
Patedam 62e6284976 preparing serialization of entities 2026-09-06 12:08:31 -04:00
Patedam d7825270ee crc32: moving to constexpr to prepare for the juliet serializer 2026-09-06 12:04:13 -04:00
Patedam cb615091ca Improving entity manager and entity support for a simpler version, made some cleanup on the road to support serialization of world 2026-08-31 23:05:20 -04:00
Patedam 4180622d6a Removing named namespace from code base. 2026-08-22 21:39:21 -04:00
Patedam 678795b793 calloc to arena 2026-08-16 22:44:52 -04:00
Patedam 4d803fdf77 conversion to arena, missing get asset filename 2026-08-16 22:37:31 -04:00
Patedam a50e3c75cb conversion to arena of filesystem 2026-08-16 22:30:40 -04:00
Patedam 262d91dd49 Various conversion to memory arena and misc clean up 2026-08-16 22:10:51 -04:00
Patedam d1b7c5dbbe Adding scratch arena for main thread (and any thread in the future)
Added some function based on raddbg code for string conversion and more.
2026-08-16 18:06:46 -04:00
Patedam 3e7216f57d Modified build system to detect pch changes *gemini 2026-08-16 13:13:51 -04:00
Patedam 5566f76a94 Cleaning game.cpp removing old test code.
Created static mesh entity
Created debug bar
2026-08-10 22:12:31 -04:00
Patedam ee20c903b2 improved the shader of mesh renderer to make the global lighting better 2026-08-09 18:04:46 -04:00
Patedam e14931b27b Merge remote-tracking branch 'origin/main' 2026-08-09 18:00:16 -04:00
Patedam 5bc4375265 Added way to set global light from game.
+ Some clean up in the mesh renderer
2026-08-09 18:00:05 -04:00
Patedam fb020d16e5 Some clean up in the mesh renderer 2026-08-09 17:59:37 -04:00
Patedam 73aa4a846b Entity now can be linked to a mesh.
Only support cube generated from code no light no nothing
2026-08-09 17:20:38 -04:00
Patedam 30ea435b87 First iteration of mesh assets 2026-08-09 16:43:35 -04:00
Patedam 697579e72f Fixing adaptive incremental build.
Moved more functions into engine.cpp
2026-08-09 15:48:53 -04:00
Patedam 7c18804df6 Fixing debug display with depth test enabled 2026-08-09 15:28:41 -04:00
Patedam 430cf35345 build fix update 2026-08-09 13:18:19 -04:00
Patedam 1a56c9e99c Added a debug controller
Made a default template for cameras.
Added delta to mouse state so we can check delta for debug Camera.
2026-08-09 13:18:04 -04:00
Patedam ae790f5a9e moved delta time computation to the HAL 2026-08-09 11:32:06 -04:00
Patedam d52c77a017 Support of IsKeyPressed that only trigger for the frame it is pressed on. 2026-08-09 11:23:56 -04:00
Patedam 681267f445 removing old vcxproj that was forgotten 2026-08-09 09:54:40 -04:00
Patedam 8582435f21 Shuffled stuff around to make it more efficient and remove redundancy 2026-08-08 23:28:36 -04:00
Patedam ab158e8b3d Merged all d3d12 files into one file 2026-08-08 23:06:29 -04:00
Patedam 403e70b14d fixing random build issue 2026-08-08 18:39:17 -04:00
Patedam 9de4fb5689 renaiming display_private.h to display_cpp.h 2026-08-08 18:05:55 -04:00
Patedam aa61f3e57c fix intellisnse when generating project 2026-08-08 14:07:02 -04:00
Patedam ba563ca642 moving project files into a separate folder.
deleting remaining bff
ignoring project files from git
2026-08-08 13:05:46 -04:00
Patedam 6c7b7c0124 Removing debug code (put in #if 0 for now)
Added a very simple camera system.
2026-08-05 19:12:34 -04:00
Patedam f122533f92 fixed build_game to use the adaptive build properly 2026-08-04 21:24:10 -04:00
Patedam cb2114daca Cleaned up build.bat to be more readable and easier to maintain 2026-08-04 21:18:05 -04:00
Patedam 3065441989 adaptive unity build 2026-08-04 20:53:14 -04:00
Patedam 5eb8fb7e9d use pch on the project. Removed all include that are now not necessary 2026-08-04 19:31:31 -04:00
Patedam aabe585696 Move shaders to arena.
For now it will use the driver Arena.
They should use an "asset" arena eventually
2026-08-03 20:00:36 -04:00
Patedam e325668650 fixing build issues 2026-08-02 19:53:41 -04:00
Patedam 2d76955cfe removing include 2026-08-02 19:46:40 -04:00
Patedam 40cb95bc40 fix generate to move imgui vc project out of imgui folder to stop confusing git 2026-08-02 19:46:08 -04:00
Patedam 00036950d6 Converted d3d12 buffers to arena. Using linked list as i'm not sure how many buffers i will use total 2026-08-02 19:33:20 -04:00
Patedam 5862843a40 Convert bunch of alloc into arena push.
Made most of the array of pointer fixed size. Could use linked list if needed.
2026-08-02 18:28:51 -04:00
Patedam 0bb2d8d9dc replaced alloca by temp arena 2026-08-01 19:49:03 -04:00
Patedam 9f6064995f Reduced Imgui memory usage by implementing a free node list. 2026-08-01 19:16:02 -04:00
Patedam 7b21ca6b1d Adapting arena push to support format as debug tag
Changed some d3d12 to work better with arenas
2026-07-29 14:44:16 -04:00
Patedam 4dc14a04b3 removing useless "realloc" feature in memory arenas 2026-07-28 16:00:39 -04:00
Patedam 4081539ee4 Using memory arena in the game that supports hot reload 2026-07-28 15:44:41 -04:00
Patedam 39586c968a Removing editor word from JulietApp 2026-07-27 16:59:28 -04:00
Patedam f0a7ce2254 removing useless C++ classes 2026-07-27 16:27:58 -04:00
Patedam 218a1d00ac Removed fastbuild completely and converted the last two remaining scripts 2026-07-27 15:47:47 -04:00
Patedam c3c245335d plop 2026-07-27 15:32:51 -04:00
Patedam b196641f18 made build system more agnostic to the projet it builds 2026-07-27 15:16:39 -04:00
Patedam 0d6b196ce6 removed most stuff in bff only kep way to generate solution 2026-07-23 20:29:20 -04:00
Patedam 4096e9d1ba Made build_system create the unity build itself, removing fastbuild dependency for that
fastbuild still used for generating solution
2026-07-23 19:41:52 -04:00
Patedam f471c447d8 converted build system to simpler C 2026-07-23 17:18:20 -04:00
Patedam 36c9ef132d updates build_system.cpp to be more independant 2026-07-23 16:44:42 -04:00
Patedam f31c9d4a6b removing useless comment from build.bat 2026-07-23 16:12:43 -04:00
Patedam 6afd675846 no need to use juliet obj everywhere, compile the dll directly 2026-07-23 16:07:03 -04:00
Patedam cf75bf2dc7 fix build so that imgui is built for anything needing it and juliet is build as dll 2026-07-23 15:15:32 -04:00
Patedam 5843ec510c make build go fast 2026-07-23 14:09:52 -04:00
Patedam 315f3d5163 basic incremental build 2026-07-23 13:00:01 -04:00
Patedam b17960b0e4 generate project for easy editing 2026-07-23 12:37:47 -04:00
Patedam 9f698bcb7f improve build.bat 2026-07-23 12:00:01 -04:00
Patedam b334273313 use build .bat to build faster 2026-07-23 11:25:15 -04:00
Patedam 550089b42e fix build error 2026-07-23 10:51:21 -04:00
Patedam 973f529741 fix build error in msvc 2026-07-23 10:50:28 -04:00
Patedam aea562ae18 removed typeid 2026-07-22 22:44:43 -04:00
Patedam 9c8f9bff41 Simplified VectorArena to always take an arena and to not support change of capacity 2026-07-22 22:12:05 -04:00
Patedam c0d40ef3e4 Regenerated doc + final submit for now 2026-07-22 17:37:01 -04:00
Patedam c436639ddd romeo fixes again 2026-07-22 17:15:52 -04:00
Patedam f3747a7581 fixed some bugs for romeo 2026-07-22 17:12:08 -04:00
Patedam 840db1f804 updates romeo to be its own solution 2026-07-22 16:30:05 -04:00
Patedam bd3b01dea4 updating Imgui 2026-07-18 19:00:10 -04:00
Patedam 0c583b58da Addded Romeo that generates some doc. WIP. 2026-07-18 18:58:29 -04:00
Patedam 446670bd29 Various debug to check lighting 2026-02-23 23:13:06 -05:00
Patedam 581656568b Debug options: Stop moving light, camera and cubes.
F1 to have free camera
2026-02-23 22:05:46 -05:00
Patedam 3277624ec2 Support lighting using a graphics buffer that is kep open! 2026-02-23 19:24:57 -05:00
Patedam 4312ecd172 Fix crash on linux with proton 2026-02-22 22:22:42 -05:00
Patedam 1056301981 Fix some errors with meshrenderer moving to engine that gemini didnt have time to fix
Will have to resync how to load more meshes. Probably need a command to do it but for now not needed.
2026-02-22 17:47:46 -05:00
Patedam 932c45d844 New skyboxrenderer + moved meshrenderer inside engine and out from main to clean that up 2026-02-22 17:16:21 -05:00
Patedam a781facd48 Factorized Push Data 2026-02-22 15:54:09 -05:00
Patedam 1e1ec84fa1 Added normals to vertexdata 2026-02-22 15:21:35 -05:00
Patedam f98be3c7f3 Made a ship version
Remove imgui from ship release, script to export fast. Can read assets from dev folder and ship folder.
2026-02-22 14:19:59 -05:00
Patedam bc6ce3afb6 remove log file 2026-02-22 12:22:34 -05:00
Patedam 816fdc27b1 Fixed the weird stutter and added fps in title bar 2026-02-22 12:22:06 -05:00
Patedam b38cc5e3d5 Fix release 2026-02-22 12:08:03 -05:00
Patedam 431015f009 Added rotation functions for matrix 2026-02-22 11:24:59 -05:00
Patedam a7947bfa17 Made MeshRenderer able to take a matrix transform for each mesh, and we are now able to draw 100 cubes.
Claude OPus helpes with implementation
2026-02-21 23:27:27 -05:00
Patedam 8e83cd32f6 Fixed MeshRenderer, our cube is now totally drawn by mesh renderer ! 2026-02-21 22:45:20 -05:00
Patedam 2362cefbc0 Fixed imgui and debug display renderer (nothing was actually broken but request were never flushed ... ) 2026-02-21 18:35:39 -05:00
Patedam bbd1095227 Some bug fix + debug level log that is not displayed by default 2026-02-21 18:16:13 -05:00
Patedam 49fc0a24d1 Meshrenderer first step. Adding logs to debug a weird crash since the add of mesh renderer and some clean up. 2026-02-21 14:04:02 -05:00
Patedam e1eb9976ad na 2026-02-16 11:14:51 -05:00
Patedam 3492cf52d8 Merge remote-tracking branch 'origin/main' 2026-02-16 11:14:32 -05:00
Patedam 762cfe52cf Fix autoclose command + condition to pause the console only if no debugger and no autoclose 2026-02-16 11:14:21 -05:00
Patedam 7a5bb0526f Fix autoclose command + condition to pause the console only if no debugger and no autoclose 2026-02-16 11:13:53 -05:00
Patedam b6e9d95552 misc change 2026-02-16 10:38:02 -05:00
Patedam 87831d0fd6 Added basic concept of Mesh
Right now can create a quad or a cube.
Need a mesh renderer taht keep the buffer and all to optimally handle the mega buffer.
2026-02-15 17:44:48 -05:00
Patedam c2a5cb84b2 made claude opus update the skills and workflow 2026-02-15 13:35:49 -05:00
Patedam 4ca3ef1706 Added temp arena and use it in hot reload 2026-02-15 13:33:27 -05:00
Patedam 38f8662d80 Fixing some VectorArena to use external arena and reduce the number of alloc.
Made the passing of name to arena more flexible
2026-02-14 22:28:32 -05:00
Patedam 36ee786128 Delete current_run.log 2026-02-14 21:28:22 -05:00
Patedam 60e1656d88 Delete test.txt 2026-02-14 21:28:13 -05:00
Patedam f8fd1dc05e Merge pull request 'removing-memory-arena' (#2) from removing-memory-arena into main
Reviewed-on: #2
2026-02-14 21:26:10 -05:00
Patedam dfd7279e84 Fix build error in release 2026-02-14 18:12:19 -05:00
Patedam 5a22a172a6 Removed the old Memory Arena and converted all to the new one 2026-02-14 18:09:47 -05:00
Patedam 6260d9aacf Squashed commit of the following:
commit c138fe98ce41a7ee9f2d208f6e38c1ed2f2aad0e
Author: Patedam <pgillen.pro@gmail.com>
Date:   Sat Feb 14 16:47:51 2026 -0500

    Updated the memory viewer to have better naming, tooltip, zoom, etc.
    Made by claude opus

commit 16120dd8651f44187192afd7943c651dd18db8f4
Author: Patedam <pgillen.pro@gmail.com>
Date:   Sat Feb 14 15:57:01 2026 -0500

    Made memory arena debugger show the new arenas. Made by gemini
2026-02-14 16:48:54 -05:00
Patedam be113fa89d Cleaned up vector unit test useless lines 2026-02-14 11:30:02 -05:00
Patedam 96169123dd Merge pull request 'Reallocate-Arena' (#1) from Reallocate-Arena into main
Reviewed-on: #1
2026-02-14 11:24:29 -05:00
Patedam 5337d4cb66 Made gemini improve and fix unit tests
Found and fixed some bugs at the same time
2026-02-13 23:00:14 -05:00
Patedam c6552d2def Allow reallocating when we use external arena.
Internal arena do not need reallocate as it is consecutive memory
2026-02-13 22:02:11 -05:00
Patedam 6a6f34516c Fixed push back 2026-02-13 21:56:27 -05:00
Patedam 536f6c5c60 Using reallocate for Vector. WIP, unit test not passing 2026-02-12 21:51:15 -05:00
Patedam f73b8284bb Adding base to reallocate + unit test 2026-02-10 23:01:42 -05:00
Patedam 679edf48ed Converted everything that was using the old "EngineArena" to our new Arena type 2026-02-08 22:21:31 -05:00
Patedam 0876ed8205 Port all window management to the new memory arena 2026-02-08 15:01:09 -05:00
Patedam 16c3a6052f Adding more unit tests and fixing the last errors 2026-02-07 23:12:48 -05:00
Patedam 76f094de4f fixing includes 2026-02-07 22:36:47 -05:00
Patedam f58a856308 Merge remote-tracking branch 'origin/main' 2026-02-07 22:28:50 -05:00
Patedam 0a2310d18f clean vector unit tst 2026-02-07 22:28:33 -05:00
Patedam 1d2b48c89e Supprimer log_direct.txt 2026-02-07 22:27:43 -05:00
Patedam 3edd23996e Unit test for vector 2026-02-07 22:25:32 -05:00
Patedam 6f4fc75b66 Added support for easily adding unit tests.
made by gemini, there is a mistake vector test are verifying std, but will be changed next iteration
2026-02-07 18:22:45 -05:00
Patedam 84fe295e18 Vector class using arena 2026-02-07 18:06:09 -05:00
Patedam 84f82ba478 More memory arena unit test
Starting to converting the display system to the new memory arena.
2026-02-07 16:21:18 -05:00
Patedam 4d66261c9f Converted log manager to use arenas 2026-02-02 22:13:50 -05:00
Patedam 45ef1134f7 Updating memory arena by hand because Gemini did something weird.
Added some os functions for memory handling.
Starting conversion to memory arena
2026-01-31 23:20:26 -05:00
Patedam 0a75554071 Updated memory debugger to have a better display of memory
Made with gemini
2026-01-25 15:19:39 -05:00
Patedam 3dd0a4a6f1 feat: Implement a memory arena system with an ImGui-based visual debugger for allocation visualization.
Made using Gemini
2026-01-25 12:14:06 -05:00
Patedam 0788fdeb98 Upgraded MemoryArena to allow poping memory and creating an arena for a specific part and not pushing completely on the same stack.
Made with Gemini
+ Updates skills to have better agents.
2026-01-25 11:01:32 -05:00
Patedam 73db1174f2 feat: add initial skill definitions for debugger, C++ game engine, graphics, and video game tester roles. 2026-01-24 18:28:41 -05:00
Patedam d4e4229d61 Fixing the D3D12DescriptorHeap.cpp memory leak. Was creating descriptor heap every time a pipeline was bound when we only need it once per commandlist.
fixed some vibe code weird shit
2026-01-23 21:43:48 -05:00
Patedam c10d371836 Cleaned up memory arena debugger + tag.
WIP
2026-01-21 20:36:38 -05:00
Patedam a41a5e6b20 generated solution 2026-01-21 19:05:34 -05:00
Patedam c5af6850c2 Moved imgui bff to external folder and not in the submodule (oops) 2026-01-21 19:04:53 -05:00
Patedam 891c404889 Added debug renderer + imgui renderer
All code made by gemini with some help
2026-01-20 22:46:10 -05:00
Patedam 0687ce5af1 MemoryArena updates using Gemini. Add some debug tag so we can debug memory allocations 2026-01-20 22:16:04 -05:00
Patedam d2b91c46d4 Misc changes
Update gitignore + adding some agent skills and tweaking workflows.
Also updated some scipts
2026-01-20 22:14:54 -05:00
Patedam f11f02d0c7 Create Imgui.md 2026-01-20 22:11:43 -05:00
Patedam 8d29fbd111 Refactored dx12 to use c++ versions of the api and not C + various tweaks (using antigravity + gemini) 2026-01-20 22:11:41 -05:00
400 changed files with 37364 additions and 100919 deletions

No files matched your search

+23 -4
View File
@@ -2,12 +2,31 @@
trigger: always_on
---
Code compiles with all warning active and warning as errors.
use static_cast or reinterpret_cast but not parenthesis for casting.
No exceptions
Use [[nodiscard]]
Use [[nodiscard]] when risk of memory leak (anything returning pointer)
auto is allowed but when its a pointer add the * and when reference adds the &
Member variable are CamelCase
Types are CamelCase
Functions are CamelCase.
Add Assert to make sure all assumptions are good. Parameters of functions for example should be verified with Assert.
Code should be self commented using proper variable names, types and functions. No need to add comments most of the time, unless the algorithm is very complex and hard to read.
When creating a new system framework, make a unit test. To make the unit test we should not modify the framework code for special unit test case.
Always put braces for if,else,for,while etc.
Coding Style:
Note: The code base do not fully use those, if in doubt, use this style and not legacy style.
Types (Structs/Enums/Unions) : PascalCase. Exemple : Arena, String8, DbgTarget, EvalValue
Primitive Types : lower_snake_case. Exemple: uint8, uint16, uint32, uint64, size_t, index_t, int32...
Function Names: lower_snake_case. Exemple: str16_from_8.
Namespace: do not use c++ namespace, add PascalCase prefix to function names. Exemple: W32_do_something
Local Variables and parameters: lower_snake_case. Exemple: arena, str, data.
Struct members: lower_snake_case. Exemple: data, node, next.
global/static variables: lower_snake_case prefixed. Exemple: g_my_global.
const variables: lower_snake_case prefixed. Exemple: k_my_const_var.
Defines and Macros: ALL_CAPS_SNAKE. Exemple: MY_MACRO
Modular Architecture & Include Rules:
- Juliet uses a single-pass unity build pattern.
- Layer aggregators `<layer>_inc.h` and `<layer>_inc.c` dictate header and source inclusion order.
- Top-level translation units (e.g. `appbin_main.c`) include the layer `_inc` files in dependency order (`core` -> `render` -> `ui`...).
- Sub-files within a module (e.g. `core.h`, `core_arena.c`, `render.c`) must NEVER directly `#include` other module headers, sibling headers, or configuration defines (`core_defines.h`).
- Never add `#include` directives to sub-files to satisfy standalone LSP/compiler diagnostics. Tooling (`new_project_gen.c` / `compile_commands.json`) must supply preceding includes dynamically based on the base file include order.
- Always explain root causes and confirm with the user before editing project architecture or files.
@@ -1,6 +1,8 @@
---
name: C++ Game Engine Programmer
name: cpp-game-engine-programmer
description: An expert C++ systems programmer specialized in game engine architecture, memory management, and D3D12 graphics.
trusted_commands:
- "misc\\agent_build.bat *"
---
# C++ Game Engine Programmer Skill
@@ -8,21 +10,16 @@ description: An expert C++ systems programmer specialized in game engine archite
## Role
You are a senior engine architect for the Juliet project. Your expertise lies in high-performance C++ systems programming, specifically within the context of game engine development. You value performance, memory efficiency, and maintainability.
## Coding Guidelines
You must follow the project's `coding-guidelines.md` (always loaded). Do not deviate from any rule. Pay special attention to `static_cast`/`reinterpret_cast` (never C-style casts), `auto*`/`auto&`, mandatory braces, and `[[nodiscard]]` when needed.
## Focus Areas
1. **High Performance**: Always consider cache locality and CPU cycle cost.
2. **Memory Management**: Prioritize manual memory management using Arenas (`MemoryArena`, `EngineArena`) over standard library containers or raw `new`/`delete`.
3. **Unified Style**: Enforce the project's coding style strictly.
## Coding Guidelines
You must always follow the project's `coding-guidelines.md`. Key tenets include:
- **No Exceptions**: Use asserts and return codes.
- **[[nodiscard]]**: Use this attribute aggressively for functions returning values.
- **Type Safety**: Use strong types (CamelCase).
- **Asserts**: Validate all assumptions, especially function parameters (`ASSERT`).
- **Self-Documenting Code**: Prefer clear naming over excessive comments.
## Workflows
- **Building**: Use the `/build` command (FastBuild) to compile the project.
- **Building**: Use the `/build` workflow (FastBuild) to compile the project.
- **Shaders**: If you modify HLSL files, use `/recompile_shaders` to update the shaders.
- **Unit Tests**: When creating a new system, always implement a unit test. Do not modify the framework solely for testing; use dependency injection or mock interfaces where appropriate.
@@ -0,0 +1,22 @@
---
name: debugger-programmer
description: An expert C++ systems programmer specialized in game engine debugging
trusted_commands:
- "misc\\agent_build.bat *"
---
# C++ Game Engine Debugger Skill
## Role
You are a senior engine architect for the Juliet project. Your expertise lies in debugging C++ game engines. You add logs and use debug tricks to find the root cause of issues.
## Coding Guidelines
You must follow the project's `coding-guidelines.md` (always loaded). Do not deviate from any rule. Even in debug/diagnostic code, use proper casts, braces, and `[[nodiscard]]` when needed.
## Workflows
- **Building**: Use the `/build` workflow to compile the project.
- **Launching**: Use the `/launch` workflow to run the application and check for issues.
- **Unit Tests**: After finding an issue, suggest unit tests to detect the issue in the future.
## Tone
Professional, technical, and precise. Focus on explaining your debugging strategy.
+12 -9
View File
@@ -1,6 +1,9 @@
---
name: Graphics Programmer & Tech Artist
name: graphics-programmer
description: An expert in 3D rendering, shader development (HLSL), and visual aesthetics, acting as a bridge between technical implementation and artistic vision.
trusted_commands:
- "misc\\agent_build.bat *"
- "misc\\recompile_shaders.bat *"
---
# Graphics Programmer & Tech Artist Skill
@@ -8,6 +11,13 @@ description: An expert in 3D rendering, shader development (HLSL), and visual ae
## Role
You are the bridge between code and art for the Juliet project. You are responsible for the final pixels on the screen, ensuring they are both performant and visually stunning. You speak fluent C++ (D3D12) and HLSL.
## Coding Guidelines
You must follow the project's `coding-guidelines.md` (always loaded). Do not deviate from any rule. This applies to both C++ (D3D12) code and HLSL shaders.
### HLSL Specifics
- Use meaningful variable names.
- Group constant buffers logically by frequency of update (Global, PerObject, etc.).
## Focus Areas
1. **Visual Excellence**: Never settle for "it works." Make it look "premium." Use lighting, shadows, and post-processing to enhance the aesthetic.
2. **Pipeline Mastery**: Deep understanding of the D3D12 pipeline (PSOs, Root Signatures, Resource Barriers, Descriptor Heaps).
@@ -15,16 +25,9 @@ You are the bridge between code and art for the Juliet project. You are responsi
## Workflows
- **Shader Iteration**: Use the `/recompile_shaders` workflow immediately after editing any `.hlsl` file to catch syntax errors early.
- **Building**: Use the `/build` workflow to compile C++ changes.
- **Performance**: Be mindful of GPU bandwidth and generic overdraw.
- **Debugging**: Think like a frame capture (PIX/RenderDoc). Visualize data (normals, depth, etc.) if unsure.
## Coding Guidelines
- **HLSL**:
- Use meaningful variable names.
- Group constant buffers logically by frequency of update (Global, PerObject, etc.).
- **C++ (D3D12)**:
- Follow the general `coding-guidelines.md`.
- Ensure correct `D3D12_RESOURCE_BARRIER` usage to avoid race conditions.
## Tone
Creative, collaborative, and highly technical. You are excited about graphics techniques (PBR, Raytracing, etc.) but grounded in the reality of shipping a performant frame.
+8 -2
View File
@@ -1,6 +1,9 @@
---
name: Video Game Tester
name: video-game-tester
description: A rigorous QA specialist and test automation engineer focused on verifying game stability and correctness.
trusted_commands:
- "misc\\agent_build.bat *"
- "misc\\launch.bat *"
---
# Video Game Tester Skill
@@ -8,6 +11,9 @@ description: A rigorous QA specialist and test automation engineer focused on ve
## Role
You are the gatekeeper of quality for the Juliet project. Your job is to break the game, find edge cases, and ensure that every change works as intended before it receives a seal of approval.
## Coding Guidelines
You must follow the project's `coding-guidelines.md` (always loaded). Do not deviate from any rule, even when writing test harness or verification code.
## Focus Areas
1. **Verification**: Never assume a change works. Always run the game.
2. **Reproduction**: If you find an issue, define clear steps to reproduce it.
@@ -17,7 +23,7 @@ You are the gatekeeper of quality for the Juliet project. Your job is to break t
- **Always Run the Game**: After any code change, even minor ones, verify by running the build.
- **Launch Command**: Use the `/launch` workflow.
- **IMPORTANT**: When running automated or quick verification, ALWAYS use the `autoclose` parameter (e.g., `/launch autoclose`) so the game exits automatically after the test sequence.
- **Log Analysis**: strict checking of `OutputDebugString` or log files for any `ERROR` or `WARNING` lines.
- **Log Analysis**: Strict checking of `misc\agent_output.log` for any `ERROR` or `WARNING` lines after a build or launch.
- **Visual Inspection**: Report any visual artifacts, flickering, or incorrect rendering immediately.
## Reporting
+10 -11
View File
@@ -1,16 +1,15 @@
---
description: Build the Juliet project using FastBuild
description: Build the Juliet projects
---
// turbo-all
This workflow sets up the Juliet build environment and runs `fbuild`.
1. To build a specific configuration (e.g., msvc-Debug):
`cmd /c "misc\shell.bat & fbuild msvc-Debug"`
2. To build the default clang:
`cmd /c "misc\shell.bat & fbuild clang"`
This workflow builds the Juliet project.
1. To build the default (clang-Debug):
// turbo
`misc\build.bat clang`
2. To build a specific configuration (e.g., clang-Debug, msvc-Debug):
// turbo
`misc\build.bat clang-Debug`
3. To see all available targets:
`cmd /c "misc\shell.bat & fbuild -showtargets"`
// turbo
`misc\build.bat -showtargets`
+9 -2
View File
@@ -2,5 +2,12 @@
description: Launch the Juliet application
---
1. Run the launch script
misc\launch.bat autoclose
// turbo-all
This workflow launches the Juliet application and writes output to `misc\agent_output.log`.
1. Run the launch script:
// turbo
`misc\launch.bat autoclose 2>&1 | tee misc\agent_output.log`
2. Check launch results by reading `misc\agent_output.log`.
+6 -2
View File
@@ -1,9 +1,13 @@
---
description: Recompile shaders for the Juliet project
---
// turbo-all
This workflow recompiles all shaders using the `recompile_shaders.bat` script.
This workflow recompiles all shaders and writes output to `misc\agent_output.log`.
1. Recompile all shaders:
`cmd /c "misc\shell.bat & misc\recompile_shaders.bat"`
// turbo
`misc\recompile_shaders.bat 2>&1 | tee misc\agent_output.log`
2. Check shader compilation results by reading `misc\agent_output.log`.
+5 -6
View File
@@ -44,10 +44,14 @@ IncludeCategories:
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentAccessModifiers: false
IndentCaseBlocks: true
IndentPPDirectives: AfterHash
IndentWidth: 4
InsertNewlineAtEOF: true
MacroBlockBegin: ''
MacroBlockEnd: ''
Macros:
- 'EachIndex(it, count)=(uint64 it = 0; it < (count); ++it)'
- 'EachNode(it, Type, first)=(Type* it = first; it != 0; it = it->next)'
NamespaceIndentation: All
ObjCSpaceAfterProperty: true
PenaltyBreakBeforeFirstCallParameter: 100
@@ -57,15 +61,10 @@ PenaltyBreakString: 100
PenaltyExcessCharacter: 1
PenaltyReturnTypeOnItsOwnLine: 1000
PointerAlignment: Left
PPIndentWidth: 1
SortIncludes: CaseInsensitive
SpacesInAngles: Never
SpacesInContainerLiterals: false
Standard: Cpp11
StatementMacros:
- UPROPERTY
- UCLASS
- USTRUCT
- GENERATED_BODY
- UENUM
TabWidth: 4
...
+34
View File
@@ -0,0 +1,34 @@
CompileFlags:
Add:
- --driver-mode=cl
- -Wno-unknown-pragmas
- -Wno-unused-command-line-argument
- -Wno-unused-function
- -Wno-undefined-internal
- -Wno-initializer-overrides
- /IJuliet
Remove:
- /Yu*
- /Yc*
- /Fp*
- /MP
- /Zi
- /Gw
- /Gy
- /Bt+
- /EHa-
Diagnostics:
UnusedIncludes: None
Suppress:
- unused-function
- undefined-internal
- initializer-overrides
InlayHints:
Enabled: true
ParameterNames: true
DeducedTypes: true
Designators: true
BlockEnd: false
TypeNameLimit: 24
+14
View File
@@ -9,10 +9,17 @@
# Binaries
[Bb]in/
[Bb]uild/
[Ll]ib/
.idea/
.vs/
vs/
[Ii]ntermediate/
[sS]hip/
# Logs
build_*.txt
launch_*.txt
# Prerequisites
*.d
@@ -47,3 +54,10 @@
*.out
*.app
*.log
misc/agent_error.log
misc/agent_output_ship.log
# Clangd
compile_commands.json
.cache/
+11
View File
@@ -1,3 +1,14 @@
[submodule "External/Imgui"]
path = External/Imgui
url = https://github.com/ocornut/imgui.git
# Pinned to v1.93.0 (c1aaac65e)
[submodule "Juliet/external/xxHash"]
path = Juliet/external/xxHash
url = https://github.com/Cyan4973/xxHash.git
# Pinned to v0.8.4 (c87183a77)
[submodule "Juliet/external/stb_sprintf"]
path = Juliet/external/stb_sprintf
url = https://github.com/nothings/stb_sprintf.git
# Pinned to v1.10 / commit 2ffb3379e
+86
View File
@@ -0,0 +1,86 @@
# ImGui Integration & Memory Debugger
## Task List
- [ ] **Preparation**
- [x] Fetch Dear ImGui Source Files (Verified existing submodule)
- [x] Create `External/imgui/Imgui.bff`
- [x] Update `fbuild.bff` to include `Imgui.bff`
- [x] Update `Juliet.bff` to link `ImGui` and include paths
- [x] Update `D3D12DescriptorHeap` to support ImGui (SRV for Font)
- [ ] **Memory Arena Debugging**
- [x] Modify `MemoryArena` struct to store allocation tags (Debug builds only)
- [x] Update `ArenaPush` signature to accept `const char* tag`
- [x] Update call sites (default parameter should minimize churn)
- [ ] **ImGui Core & TDD**
- [x] Create `Core/ImGui/ImGuiService.h/cpp` (Wrapper)
- [x] Create `Core/ImGui/ImGuiTests.cpp`
- [x] Implement `ImGui::SetAllocatorFunctions` using `EngineArena`
- [x] Test context creation and IO setup
- [ ] **Integration**
- [ ] Win32: Integrate `WndProc` in `Win32DisplayEvent.cpp`
- [x] DX12: Initialize/Shutdown in `D3D12GraphicsDevice` (Implemented in `ImGuiRenderer.cpp`)
- [x] DX12: Render Frame in `D3D12GraphicsDevice` (Fixed Buffer Crash)
- [ ] **Memory Debugger**
- [x] Create `Engine/Debug/MemoryDebugger.h/cpp`
- [x] Implement `DrawMemoryArena(const char* name, MemoryArena* arena)` with Allocation Tags
- [x] Implement Hex View for Arena Data (Allocations list implemented, Hex view pending/skipped)
- [x] Integrate into Main Loop (Toggle with 'Home' key)
- [ ] **Verification**
- [ ] Launch and verify ImGui renders
- [x] Verify Memory Debugger shows allocations and tags
---
## Implementation Plan
### Goal Description
Integrate Dear ImGui into the Juliet engine.
Implement a specialized Test Driven Development (TDD) approach.
Create a Memory Debugger panel to visualize `MemoryArena` state with allocation tags.
### Proposed Changes
#### Build System
**[NEW] [Imgui.bff](file:///w:/Classified/Juliet/External/imgui/Imgui.bff)**
- Defines the `ImGui` library configuration.
- Includes `imgui.cpp`, `imgui_draw.cpp`, `imgui_tables.cpp`, `imgui_widgets.cpp`, `imgui_demo.cpp`.
- Includes backends: `imgui_impl_win32.cpp`, `imgui_impl_dx12.cpp`.
**[MODIFY] [fbuild.bff](file:///w:/Classified/Juliet/fbuild.bff)**
- Include `External/imgui/Imgui.bff`.
**[MODIFY] [Juliet.bff](file:///w:/Classified/Juliet/Juliet/Juliet.bff)**
- Add `ImGui` to `Juliet` library dependencies.
- Add `External/imgui` and `External/imgui/backends` to include paths.
#### Core/Memory
**[MODIFY] [MemoryArena.h](file:///w:/Classified/Juliet/Juliet/include/Core/Memory/MemoryArena.h)**
**[MODIFY] [MemoryArena.cpp](file:///w:/Classified/Juliet/Juliet/src/Core/Memory/MemoryArena.cpp)**
- **Struct Change**: Add `AllocationTracking` vector (likely `std::vector` wrapped or fixed size array) in `JULIET_DEBUG`.
- **Function Change**: `ArenaPush(..., const char* tag = nullptr)`.
- **Logic**: Store offset + tag on push.
#### Core/ImGui
**[NEW] [ImGuiService.h](file:///w:/Classified/Juliet/Juliet/include/Core/ImGui/ImGuiService.h)**
**[NEW] [ImGuiService.cpp](file:///w:/Classified/Juliet/Juliet/src/Core/ImGui/ImGuiService.cpp)**
- Wrapper for ImGui lifecycle.
- Uses `EngineArena` for internal ImGui allocations.
**[NEW] [ImGuiTests.cpp](file:///w:/Classified/Juliet/Juliet/src/Core/ImGui/ImGuiTests.cpp)**
- TDD suite for ImGui service.
#### Graphics/D3D12
**[MODIFY] [D3D12GraphicsDevice.cpp](file:///w:/Classified/Juliet/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp)**
- ImGui backend initialization and rendering.
#### Engine/Debug
**[NEW] [MemoryDebugger.h](file:///w:/Classified/Juliet/Juliet/include/Engine/Debug/MemoryDebugger.h)**
**[NEW] [MemoryDebugger.cpp](file:///w:/Classified/Juliet/Juliet/src/Engine/Debug/MemoryDebugger.cpp)**
- Draw arena usage bar using tracked tags.
- Draw hex dump.
+53 -34
View File
@@ -1,45 +1,64 @@
# Memory System Status & Migration Plan
# Paged Memory Arena Architecture
## Completed Work
## Status
**Implemented & Active** (Jan 2026)
### Core Systems
- **MemoryArena**: Implemented linear allocator with alignment, markers, and reset support.
- **Global Arenas**:
- `ScratchArena` (64MB): Transient per-frame memory.
- `EngineArena` (256MB): Persistent engine memory (internal).
- `GameArena` (512MB): Persistent game memory (exported to Game DLL).
- **Helpers**: Added `ArenaPushType<T>` and `ArenaPushArray<T>` with automatic zero-initialization.
## Overview
The memory system uses a **Paged Arena** model backed by global **Memory Pools**. This architecture supports indefinite growth, efficient clearing, and minimizes OS-level allocations by sub-allocating from large pre-reserved buffers.
### Migrated Subsystems
- **Display**: `Win32Window` and `Win32DisplayDevice` now use `EngineArena`.
- **Game Entities**: `Entity.h` uses `GameArena` for entity allocation; manual `free` calls removed from `game.cpp`.
- **Hot Reload**: `HotReload.cpp` (persistent paths to `EngineArena`) and `Win32HotReload.cpp` (temp paths to `ScratchArena`).
## Architecture
## Remaining Work
### 1. Memory Pool (`MemoryPool`)
A Global Source of memory blocks.
- **Role**: Manages a large contiguous memory region (e.g., 512MB for Game).
- **Implementation**: Free List Allocator.
- **Operations**: `AllocateBlock` (First-Fit with Splitting), `FreeBlock` (Returns to list).
- **Optimization**: **Block Splitting** is implemented to preserve free space. If a block in the free list is significantly larger than requested (`Alloc + Header + 16 bytes`), it is split, and the remainder is returned to the free list. This prevents pool exhaustion from small allocations consuming large blocks.
- **Instances**:
- `g_EngineMemory` (256MB)
- `g_GameMemory` (512MB)
- `g_ScratchMemory` (64MB)
The following subsystems still use legacy `malloc`/`calloc`/`realloc`/`free` and need to be migrated.
### 2. Memory Arena (`MemoryArena`)
A High-Level Allocator.
- **Structure**: A linked list of `MemoryBlocks`.
- **Behavior**:
- **Growth**: Starts with one block. If an allocation exceeds capacity, it requests a new Block (Page) from the backing Pool and links it.
- **Alloc**: Linear bump-pointer within the current block.
- **Clear**: Returns all blocks (except the first) to the Pool. Resets the first block.
- **Realloc**: Supports in-place expansion (if top of stack) or copy-and-move.
- **Instances**: `GetGameArena()`, `GetEngineArena()`, `GetScratchArena()`.
### 3. Memory Block (`MemoryBlock`)
The unit of exchange between Pool and Arena.
- **Header**: Includes `Magic` (debug safety), `Next` pointer, `TotalSize` (renamed from `Size`), and `Used` offset.
- **Alignment**: 16-byte alignment enforced.
- **Safety**: Debug builds use memory poisoning (`0xCD` on alloc, `0xDD` on free) and Magic number checks to detect corruption.
### 4. Arena Pop (`ArenaPop`)
Support for LIFO allocations (reclaiming memory).
- **Behavior**: Checks if the pointer is at the very top of the stack (`CurrentBlock->Used`).
- **Optimization**: If valid, decrements `Used` to reclaim space. If not (fragmented), does nothing.
- **Usage**: Critical for `ImGui` vector resizing to prevent exponential memory consumption.
### IO System
- **Files**: `Core/HAL/IO/IOStream.cpp`, `Core/HAL/IO/Win32/Win32IOStream.cpp`
- **Allocations**: `IOStream` instance, data buffers, `Win32IOStreamDataPayload`.
- **Challenge**: `Realloc` is used for growing buffers.
- **Strategy**:
- `IOStream` struct -> `ScratchArena` (if transient) or `EngineArena`.
- Buffers: Evaluate if `ArenaPush` with large enough capacity is sufficient, or implement a growable buffer on top of arena (or use `std::vector` with custom allocator if absolutely needed, but prefer simple fixed max size if possible).
## Usage
### Graphics / Debug
- **Files**: `Graphics/DebugDisplayRenderer.cpp`
- **Allocations**: `DepthTestedVertices`, `OverlayVertices`.
- **Strategy**: Use `EngineArena` or a dedicated `RenderArena` if these are persistent. If per-frame, move to `ScratchArena`.
```cpp
// 1. Get an Arena
MemoryArena* arena = GetScratchArena();
### Shader Compiler
- **Files**: `JulietShaderCompiler/ShaderCompiler.cpp`
- **Allocations**: Argument arrays, file buffers.
- **Strategy**: Use `ScratchArena` for all compilation tasks as they are transient.
// 2. Push Data
MyStruct* data = ArenaPushType<MyStruct>(arena, "Tag");
void* raw = ArenaPush(arena, 1024, 16, "RawBuffer");
### Filesystem
- **Files**: `Core/HAL/Filesystem/Filesystem.cpp`
- **Allocations**: `CachedBasePath`.
- **Strategy**: Migrate to `EngineArena` (persistent).
// 3. Pop Data (LIFO)
ArenaPop(arena, raw, 1024); // Reclaims memory
// 4. Reset (Scratch only)
ScratchArenaReset(); // Returns pages to g_ScratchMemory
```
## Migration Status
- **ImGui**: Migrated to `GetEngineArena()` (Paged) with `ArenaPop` support for efficient vector resizing.
- **Display/Window**: Uses Engine Arena.
- **Game Entities**: Uses Game Arena.
+92
View File
@@ -0,0 +1,92 @@
# Workload Analysis: Removing C++ Stdlib & Migrating to Pure C
This analysis outlines the required workload, technical impact, and migration strategy for:
1. Removing all standard C++ library dependencies (`std::*`) from the Juliet codebase.
2. Moving completely from C++ to pure C.
---
## 1. Scope Overview
The repository consists of multiple sub-projects (**Juliet Core Engine**, **JulietApp**, **Game**, **Romeo**, and **External dependencies**). Eliminating stdlib dependencies versus migrating completely to pure C represent two distinct milestones:
| Metric / Domain | Task 1: Eliminate C++ stdlib | Task 2: Pure C Migration |
| :--- | :--- | :--- |
| **Language Target** | C++20 (without standard libraries) | Pure C (C11 / C99 standard) |
| **Containers & Math** | Custom templates (`Vector<T>`, `HashTable<K,V>`, custom algorithms) | C structs + `void*` / macro templates + explicit functions |
| **Threading & Sync** | Win32 / OS APIs directly (`CreateThread`, `SRWLOCK`) | Native OS APIs (`CreateThread`, `SRWLOCK`, atomic primitives) |
| **External Third-Party** | Native Direct3D 12, ImGui | `cimgui` (C bindings), DirectX C COM interfaces (`ID3D12Device*`) |
| **OOP & Templates** | Kept intact | Struct-based vtables, no function overloading, no references (`&`) |
---
## 2. Phase 1: Removing C++ stdlib Dependencies
### A. Containers & Utility Replacement
- **Current Usages**: `<vector>`, `<queue>`, `<list>`, `<forward_list>`, `<algorithm>`, `<bit>`, `<concepts>`, `<type_traits>`, `<source_location>`.
- **Workload**:
1. **Containers**: Replace container implementations (such as `Juliet/include/Core/Container/Vector.h`, which currently includes `<vector>`) with custom dynamically resizing arrays / buffer allocators backed by `MemoryArena`.
2. **Type Traits & Concepts**: Replace `<type_traits>` and `<concepts>` usage in files like `Game/Entity/Entity.h` with compiler intrinsics (e.g. `__is_enum`, `__is_base_of`) or internal lightweight trait structures.
3. **Algorithms & Math Utilities**: Implement custom `Min`, `Max`, `Clamp`, `Sort`, and `Swap` functions in `CoreUtils.h` or custom inline header helpers.
### B. Threading & Synchronization
- **Current Usages**: `<thread>`, `<mutex>`, `std::thread`, `std::mutex`, `std::lock_guard`.
- **Workload**:
1. Replace `<thread>` in `Thread.h` and `Romeo/src/main.cpp` with platform abstraction wrappers using Win32 `CreateThread` / `CloseHandle`.
2. Replace `<mutex>` in `Mutex.h` with `SRWLOCK` or `CRITICAL_SECTION`.
### C. Time & File I/O
- **Current Usages**: `<chrono>`, `<cstdio>`, `<fstream>`, `<ios>`, `<iosfwd>`, `<cstdlib>`.
- **Workload**:
1. Replace `<chrono>` in `LogManager.cpp` and `SystemEvent.cpp` with `QueryPerformanceCounter` / `QueryPerformanceFrequency` on Windows.
2. Standardize filesystem I/O on Win32 API (`CreateFileW`, `ReadFile`, `WriteFile`) or minimal C runtime headers (`<stdio.h>`, `<stdlib.h>`, `<string.h>`).
### D. Memory Allocators & RTTI
- **Current Usages**: `<new>`, `<typeinfo>` (`typeid` in `MemoryArena.h`), `<memory>`.
- **Workload**:
1. Replace placement `new` with custom inline placement functions or custom explicit initialization methods.
2. Replace `typeid` / `type_info` usages with custom type hashing or enum type IDs.
---
## 3. Phase 2: Complete Migration to C
Transitioning fully to C requires refactoring away all C++ language constructs.
### A. DirectX 12 & Agility SDK Integration
- **D3D12 Header Interface**: Switch from C++ COM smart pointers / methods (`device->CreateCommandAllocator(...)`) to standard C COM interfaces (`ID3D12Device_CreateCommandAllocator(device, ...)`).
- **D3DX12 Helper Library**: `d3dx12_state_object.h` and helper files rely heavily on stdlib (`std::vector`, `std::memory`, C++ structures). These must be rewritten in plain C or replaced with native DirectX 12 C struct initializers.
### B. UI Framework (ImGui)
- Dear ImGui is natively C++.
- **Workload**: Switch from standard `imgui.h` / `imgui_stdlib.h` to [cimgui](https://github.com/cimgui/cimgui) (C-compatible wrappers for ImGui) or maintain a minimal C++ bridge file specifically for ImGui.
### C. Codebase Refactoring (Language Paradigm)
1. **Classes to Structs**: Convert classes in `Juliet` core, `Game`, and `Romeo` to C structs.
2. **Virtual Functions**: Replace polymorphism with explicit `void*` context pointers and function pointer tables (vtables).
3. **Namespaces & Function Overloading**: Prefix function names (e.g., `Juliet_Log_Info(...)`) as C does not support namespaces or function overloading.
4. **Member Functions to Free Functions**: Convert `obj.Method(args)` to `Method(&obj, args)`.
5. **No Reference Arguments (`&`)**: Replace all pass-by-reference parameters with explicit pointers (`*`).
6. **Casting Rules**: Remove `static_cast` and `reinterpret_cast` in favor of standard C explicit casts `(Type)val`.
### D. Build System (`Romeo.bff` / `fbuild.bff`)
- Update compiler settings in FastBuild (`.bff` files) and Visual Studio project settings from `/std:c++20` to C compiler flags (`/std:c11` or `/TC`).
---
## 4. Estimated Workload Matrix
| Task Module | Complexity | Estimated Relative Effort | Risks / Key Considerations |
| :--- | :--- | :--- | :--- |
| **1. Stdlib-Free Containers & Utils** | Moderate | ~15% | Custom array/hash table implementation. |
| **2. OS Threading & Time Abstraction** | Low-Moderate | ~10% | Clean Windows API replacements (`QueryPerformanceCounter`, `SRWLOCK`). |
| **3. C Struct & Syntax Refactoring** | High | ~40% | Large volume of mechanical refactoring (references, namespaces, overloading, methods). |
| **4. Graphics / D3D12 C Refactoring** | High | ~20% | `d3dx12` heavily relies on C++ templates/stdlib; rewriting helper structs in C. |
| **5. ImGui / Romeo GUI Integration** | Moderate | ~15% | Needs `cimgui` integration or a C-wrapper boundary. |
---
## 5. Recommendation & Phasing
1. **Step 1 (Stdlib Removal)**: Keep C++20 language features (classes, templates, member methods), but remove all stdlib inclusions (`<vector>`, `<thread>`, `<chrono>`, `<algorithm>`). Replace them with custom `Juliet` engine primitives.
2. **Step 2 (Evaluate C Migration)**: Re-evaluate if moving to pure C provides enough benefit vs. keeping a clean, stdlib-free C++ subset ("C with Classes" / stdlib-free C++).
+292
View File
@@ -0,0 +1,292 @@
# Lighting & Skybox Plan
## Current State
| Item | Current |
|------|---------|
| Vertex format | `float3 Position` + `float4 Color` (28 bytes, no normals) |
| Vertex shader | [Triangle.vert.hlsl](file:///w:/Classified/Juliet/Assets/source/Triangle.vert.hlsl) — bindless buffer read, `mul(VP, mul(Model, pos))` |
| Fragment shader | [SolidColor.frag.hlsl](file:///w:/Classified/Juliet/Assets/source/SolidColor.frag.hlsl) — passthrough `return Color` |
| Push constants | `ViewProjection` + `Model` + `BufferIndex` + extras |
| Texturing | None — no sampler usage, no texture reads |
---
## Phase 1: Add Normals to Vertex Data
Lighting requires normals. This is the foundation for everything else.
### Vertex Format Change
```diff
struct Vertex
{
float Position[3];
+ float Normal[3];
float Color[4];
};
```
- **Stride**: 28 → 40 bytes
- Update `Triangle.vert.hlsl` stride constant: `uint stride = 40;`
- Load normal after position: `float3 normal = asfloat(buffer.Load3(offset + 12));`
- Load color shifts to: `float4 col = asfloat(buffer.Load4(offset + 24));`
### Files to modify
| File | Change |
|------|--------|
| [VertexData.h](file:///w:/Classified/Juliet/Juliet/include/Graphics/VertexData.h) | Add `float Normal[3]` to `Vertex` |
| [MeshRenderer.cpp](file:///w:/Classified/Juliet/Juliet/src/Graphics/MeshRenderer.cpp) | Update `AddCube()` to include per-face normals |
| [Triangle.vert.hlsl](file:///w:/Classified/Juliet/Assets/source/Triangle.vert.hlsl) | Update stride, load normal, pass to fragment |
---
## Phase 2: Basic Directional Light (Diffuse)
Simple single directional light with diffuse (Lambert) shading.
### Approach: Push light data through RootConstants
Add light direction and color to `RootConstants.hlsl` and the C++ `PushData`:
```hlsl
// RootConstants.hlsl additions
float3 LightDirection; // Normalized, world-space
float _LightPad;
float3 LightColor;
float AmbientIntensity;
```
```cpp
// PushData additions
Vector3 LightDirection;
float _LightPad;
Vector3 LightColor;
float AmbientIntensity;
```
### Shader Changes
**Vertex shader** — transform normal to world space and pass it to fragment:
```hlsl
// Triangle.vert.hlsl output struct
struct Output
{
float4 Color : TEXCOORD0;
float3 WorldNormal : TEXCOORD1;
float4 Position : SV_Position;
};
// In main():
float3 worldNormal = mul((float3x3)Model, normal);
output.WorldNormal = worldNormal;
```
> [!NOTE]
> Using `(float3x3)Model` for normal transform is only correct for uniform-scale transforms. For non-uniform scale, you'd need the inverse-transpose. Fine for now with translation-only transforms.
**Fragment shader** — apply Lambert diffuse:
```hlsl
// SolidColor.frag.hlsl → rename to Lit.frag.hlsl
#include "RootConstants.hlsl"
float4 main(float4 Color : TEXCOORD0, float3 WorldNormal : TEXCOORD1) : SV_Target0
{
float3 N = normalize(WorldNormal);
float NdotL = saturate(dot(N, -LightDirection));
float3 diffuse = Color.rgb * LightColor * NdotL;
float3 ambient = Color.rgb * AmbientIntensity;
return float4(diffuse + ambient, Color.a);
}
```
### Files to modify
| File | Change |
|------|--------|
| [RootConstants.hlsl](file:///w:/Classified/Juliet/Assets/source/RootConstants.hlsl) | Add light params |
| [MeshRenderer.h](file:///w:/Classified/Juliet/Juliet/include/Graphics/MeshRenderer.h) | Add light params to `PushData` |
| [Triangle.vert.hlsl](file:///w:/Classified/Juliet/Assets/source/Triangle.vert.hlsl) | Pass world normal to fragment |
| [SolidColor.frag.hlsl](file:///w:/Classified/Juliet/Assets/source/SolidColor.frag.hlsl) | Lambert diffuse + ambient |
| [DebugDisplayRenderer.cpp](file:///w:/Classified/Juliet/Juliet/src/Graphics/DebugDisplayRenderer.cpp) | Update push data struct to match new layout |
| [main.cpp](file:///w:/Classified/Juliet/JulietApp/main.cpp) | Set `LightDirection`, `LightColor`, `AmbientIntensity` |
---
## Phase 3: Skybox
A skybox renders a cubemap texture behind all geometry, giving the scene a background.
### Approach: Fullscreen-triangle with inverse VP
Render a fullscreen triangle as the very last thing (or first with depth write off), sample a cubemap using the camera view direction reconstructed from screen coordinates.
### New Assets
- **Skybox cubemap texture** — a `.dds` or 6 `.png` face images loaded as a `TextureCube`
- **New shaders**: `Skybox.vert.hlsl` + `Skybox.frag.hlsl`
### Shader Design
**Vertex shader** — fullscreen triangle using `SV_VertexID`:
```hlsl
// Skybox.vert.hlsl
#include "RootConstants.hlsl"
struct Output
{
float3 ViewDir : TEXCOORD0;
float4 Position : SV_Position;
};
Output main(uint vertexID : SV_VertexID)
{
Output output;
// Fullscreen triangle
float2 uv = float2((vertexID << 1) & 2, vertexID & 2);
float4 clipPos = float4(uv * 2.0 - 1.0, 1.0, 1.0);
clipPos.y = -clipPos.y;
output.Position = clipPos;
// Reconstruct view direction from clip space
// InverseViewProjection needs to be added to RootConstants
float4 worldPos = mul(InverseViewProjection, clipPos);
output.ViewDir = worldPos.xyz / worldPos.w;
return output;
}
```
**Fragment shader** — sample cubemap:
```hlsl
// Skybox.frag.hlsl
#include "RootConstants.hlsl"
float4 main(float3 ViewDir : TEXCOORD0) : SV_Target0
{
TextureCube skybox = ResourceDescriptorHeap[TextureIndex];
SamplerState samp = SamplerDescriptorHeap[0]; // Linear clamp
return skybox.Sample(samp, normalize(ViewDir));
}
```
### Pipeline Requirements
| Setting | Value |
|---------|-------|
| Depth write | **Off** (skybox is infinitely far) |
| Depth test | **LessEqual** or **Off** (render behind everything) |
| Cull mode | **None** (fullscreen triangle) |
| Draw call | `Draw(3, 0)` — no vertex buffer needed |
### New C++ Components
1. **Cubemap loading** — need to create `TextureCube` from 6 face images or a `.dds` cubemap file
2. **Skybox pipeline** — new `GraphicsPipeline` with the skybox shaders and the pipeline settings above
3. **Sampler** — need at least one linear sampler in the sampler heap (may already exist for ImGui)
4. **`InverseViewProjection`** — add to `RootConstants` and compute in C++ via a `MatrixInverse` function
> [!IMPORTANT]
> `MatrixInverse` needs to be implemented in `Matrix.h`. This is a non-trivial 4×4 matrix inversion (adjugate/determinant method or Gauss-Jordan).
### Files to modify/create
| File | Change |
|------|--------|
| [NEW] `Skybox.vert.hlsl` | Fullscreen triangle + view direction |
| [NEW] `Skybox.frag.hlsl` | Cubemap sample |
| [RootConstants.hlsl](file:///w:/Classified/Juliet/Assets/source/RootConstants.hlsl) | Add `InverseViewProjection` |
| [Matrix.h](file:///w:/Classified/Juliet/Juliet/include/Core/Math/Matrix.h) | Add `MatrixInverse()` |
| [MeshRenderer.h](file:///w:/Classified/Juliet/Juliet/include/Graphics/MeshRenderer.h) | Add `InverseViewProjection` to `PushData` |
| [main.cpp](file:///w:/Classified/Juliet/JulietApp/main.cpp) | Create skybox pipeline, load cubemap, render skybox |
---
## Recommended Implementation Order
```mermaid
graph LR
A["Phase 1<br/>Add Normals"] --> B["Phase 2<br/>Directional Light"]
B --> C["Phase 3<br/>Skybox"]
```
1. **Phase 1** (normals) is the smallest and unblocks Phase 2
2. **Phase 2** (lighting) gives the cubes visible 3D depth immediately
3. **Phase 3** (skybox) is independent of lighting but benefits from having `MatrixInverse` and sampler infrastructure
> [!TIP]
> Phases 1+2 together are a single session of work (~8 files). Phase 3 is larger due to cubemap loading and new pipeline creation.
---
## Open Questions (Answered)
1. **Cubemap source** — **Procedural gradient skybox** chosen because asset loading infrastructure is not yet established.
2. **Sampler heap** — Evaluate what exists. However, with a procedural skybox, we won't need a sampler for Phase 3! (The sky color can be procedurally generated from the reconstructed view direction).
3. **Specular** — **Blinn-Phong** specular, structured in an agnostic way so PBR (physically based rendering) parameters can be plugged in later.
---
## Phase 4: Forward Rendered Entity Lights
A simple, fast forward renderer using a Global Structured Buffer for entity-based point lights.
### Approach: Bindless Global Lights Buffer
Instead of passing each light via PushConstants, we pass a `StructuredBuffer<PointLight>` index and iterate over the active lights in the fragment shader.
### New C++ Components
```cpp
struct PointLight
{
Vector3 Position;
float Radius;
Vector3 Color;
float Intensity;
};
```
1. **Lights Buffer Allocation**: Allocate a `StructuredBuffer` large enough for all potential lights (e.g., max 1024).
2. **Buffer Upload**: Loop through the active game entity lights every frame and upload them using the TransferBuffer system.
3. **PushData Update**:
- `uint32 LightsBufferIndex`
- `uint32 ActiveLightCount`
### Shader Design
**Fragment Shader Expansion** loop over all `ActiveLightCount` to compute the attenuation and diffuse, then accumulate.
```hlsl
StructuredBuffer<PointLight> lights = ResourceDescriptorHeap[LightsBufferIndex];
float3 totalDiffuse = Color.rgb * LightColor * NdotL; // Include Directional Sun
for (uint i = 0; i < ActiveLightCount; ++i)
{
PointLight light = lights[i];
float3 lightVector = light.Position - WorldPosition;
float distance = length(lightVector);
if (distance > light.Radius) continue;
float3 L = lightVector / distance;
float NdotL = saturate(dot(N, L));
float attenuation = 1.0 - saturate(distance / light.Radius);
attenuation *= attenuation; // inverse square-ish
totalDiffuse += Color.rgb * light.Color * light.Intensity * NdotL * attenuation;
}
```
> [!NOTE]
> Passing `WorldPosition` to the Fragment shader from the Vertex shader is required for positional lighting.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
; Paul
1
+4 -1
View File
@@ -13,9 +13,12 @@ Output main(uint vertexIndex : SV_VertexID)
// Retrieve the vertex buffer using SM6.6 bindless syntax
ByteAddressBuffer buffer = ResourceDescriptorHeap[BufferIndex];
// VertexOffset is used as vertex offset for consolidated buffer (depth-tested at 0, overlay at halfMax)
uint actualVertexIndex = vertexIndex + VertexOffset;
// Vertex layout: float3 Position (12 bytes) + float4 Color (16 bytes) = 28 bytes stride
uint stride = 28;
uint offset = vertexIndex * stride;
uint offset = actualVertexIndex * stride;
float3 pos = asfloat(buffer.Load3(offset));
float4 col = asfloat(buffer.Load4(offset + 12));
+14
View File
@@ -0,0 +1,14 @@
struct Input
{
float4 Color : TEXCOORD0;
float2 UV : TEXCOORD1;
};
#include "RootConstants.hlsl"
float4 main(Input input) : SV_Target0
{
Texture2D<float4> tex = ResourceDescriptorHeap[TextureIndex];
SamplerState samp = SamplerDescriptorHeap[0];
return input.Color * tex.Sample(samp, input.UV);
}
+44
View File
@@ -0,0 +1,44 @@
struct Output
{
float4 Color : TEXCOORD0;
float2 UV : TEXCOORD1;
float4 Position : SV_Position;
};
#include "RootConstants.hlsl"
struct Vertex
{
float2 Pos;
float2 UV;
uint Color;
};
Output main(uint vertexIndex : SV_VertexID)
{
Output output;
ByteAddressBuffer buffer = ResourceDescriptorHeap[BufferIndex];
uint actualVertexIndex = vertexIndex + VertexOffset;
uint stride = 20;
uint offset = actualVertexIndex * stride;
float2 pos = asfloat(buffer.Load2(offset));
float2 uv = asfloat(buffer.Load2(offset + 8));
uint col = buffer.Load(offset + 16);
float4 c;
c.x = float(col & 0xFF) / 255.0f;
c.y = float((col >> 8) & 0xFF) / 255.0f;
c.z = float((col >> 16) & 0xFF) / 255.0f;
c.w = float((col >> 24) & 0xFF) / 255.0f;
output.Position = float4(pos * Scale + Translate, 0.0f, 1.0f);
output.Color = c;
output.UV = uv;
return output;
}
+32 -1
View File
@@ -1,11 +1,42 @@
#ifndef ROOT_CONSTANTS_HLSL
#define ROOT_CONSTANTS_HLSL
struct PointLight
{
float3 Position;
float Radius;
float3 Color;
float Intensity;
};
struct TransformData
{
row_major float4x4 Model;
};
cbuffer RootConstants : register(b0, space0)
{
row_major float4x4 ViewProjection;
uint MeshIndex;
uint TransformsBufferIndex;
uint BufferIndex;
uint _Padding[3];
uint TextureIndex;
uint VertexOffset; // Base vertex for indexed drawing with bindless buffers
uint LightBufferIndex;
uint ActiveLightCount;
float GlobalAmbientIntensity;
float3 GlobalLightDirection; // Normalized, world-space
uint _Pad1;
float3 GlobalLightColor;
uint _Pad2;
float2 Scale; // 2D scale factor
float2 Translate; // 2D translation
float4 MeshAlbedo;
};
#endif // ROOT_CONSTANTS_HLSL
+26
View File
@@ -0,0 +1,26 @@
#include "RootConstants.hlsl"
float4 main(float3 ViewDir : TEXCOORD0) : SV_Target0
{
float3 dir = normalize(ViewDir);
// Simple Procedural Gradient Skybox Colors
float3 skyZenithColor = float3(0.05f, 0.15f, 0.45f); // Deep blue top
float3 skyHorizonColor = float3(0.4f, 0.6f, 0.9f); // Light blue horizon
float3 groundColor = float3(0.1f, 0.1f, 0.15f); // Dark ground
float t = dir.z; // -1 to 1 based on vertical alignment
float3 finalColor = groundColor;
if (t > 0.0f) {
// Blend from horizon to zenith
finalColor = lerp(skyHorizonColor, skyZenithColor, t);
} else {
// Ground - quick blend from horizon to ground
finalColor = lerp(skyHorizonColor, groundColor, saturate(-t * 2.0f));
}
// Combine with overall ambient lighting scale
return float4(finalColor, 1.0f);
}
+74
View File
@@ -0,0 +1,74 @@
#include "RootConstants.hlsl"
struct Output
{
float3 ViewDir : TEXCOORD0;
float4 Position : SV_Position;
};
float4x4 Inverse(float4x4 m)
{
float n11 = m[0][0], n12 = m[1][0], n13 = m[2][0], n14 = m[3][0];
float n21 = m[0][1], n22 = m[1][1], n23 = m[2][1], n24 = m[3][1];
float n31 = m[0][2], n32 = m[1][2], n33 = m[2][2], n34 = m[3][2];
float n41 = m[0][3], n42 = m[1][3], n43 = m[2][3], n44 = m[3][3];
float t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44;
float t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44;
float t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44;
float t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
float det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
float idet = 1.0f / det;
float4x4 ret;
ret[0][0] = t11 * idet;
ret[0][1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * idet;
ret[0][2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * idet;
ret[0][3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * idet;
ret[1][0] = t12 * idet;
ret[1][1] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * idet;
ret[1][2] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * idet;
ret[1][3] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * idet;
ret[2][0] = t13 * idet;
ret[2][1] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * idet;
ret[2][2] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * idet;
ret[2][3] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * idet;
ret[3][0] = t14 * idet;
ret[3][1] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * idet;
ret[3][2] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * idet;
ret[3][3] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * idet;
return ret;
}
Output main(uint vertexID : SV_VertexID)
{
Output output;
// Fullscreen triangle properties
// vertexID 0 -> uv(0,0) -> pos(-1, 1)
// vertexID 1 -> uv(2,0) -> pos( 3, 1)
// vertexID 2 -> uv(0,2) -> pos(-1, -3)
float2 uv = float2((vertexID << 1) & 2, vertexID & 2);
// Map uv to clip space position.
// Z is set to 1.0 (far plane in reverse-Z or standard depth)
// Assuming standard Z projection ranges from 0 to 1, we use 1.0 for the far plane.
// Depending on reverse-z, this might need to be 0.0, but let's stick to standard 1.0
// depth for skyboxes.
float4 clipPos = float4(uv * 2.0 - 1.0, 1.0, 1.0);
clipPos.y = -clipPos.y; // Flip Y for D3D
output.Position = clipPos;
// Reconstruct view direction from clip space
float4x4 inverseVP = Inverse(ViewProjection);
float4 worldPos = mul(inverseVP, clipPos);
output.ViewDir = worldPos.xyz / worldPos.w;
return output;
}
+49 -2
View File
@@ -1,4 +1,51 @@
float4 main(float4 Color : TEXCOORD0) : SV_Target0
#include "RootConstants.hlsl"
struct Input
{
return Color;
float4 Color : TEXCOORD0;
float3 WorldNormal : TEXCOORD1;
float3 WorldPosition : TEXCOORD2;
};
float4 main(Input input) : SV_Target0
{
float3 normal = normalize(input.WorldNormal);
// Initial ambient component (Soft blue sky ambient)
float3 ambientColor = float3(0.6f, 0.7f, 0.9f);
float3 result = input.Color.rgb * ambientColor * GlobalAmbientIntensity;
// Directional light contribution
float3 sunDir = normalize(-GlobalLightDirection);
float ndotl = max(dot(normal, sunDir), 0.0);
result += input.Color.rgb * GlobalLightColor * ndotl;
// Point lights
if (ActiveLightCount > 0)
{
StructuredBuffer<PointLight> pointLights = ResourceDescriptorHeap[LightBufferIndex];
for (uint i = 0; i < ActiveLightCount; ++i)
{
PointLight light = pointLights[i];
float3 lightDir = light.Position - input.WorldPosition;
float dist = length(lightDir);
if (dist < light.Radius)
{
lightDir = normalize(lightDir);
float attenuation = 1.0 - (dist / light.Radius);
attenuation = max(attenuation, 0.0);
float pndotl = max(dot(normal, lightDir), 0.0);
result += light.Color * input.Color.rgb * pndotl * attenuation * light.Intensity;
}
}
}
// Simple Gamma correction
result = pow(result, 1.0 / 2.2);
return float4(result, input.Color.a);
}
+18 -12
View File
@@ -1,6 +1,8 @@
struct Output
{
float4 Color : TEXCOORD0;
float3 WorldNormal : TEXCOORD1;
float3 WorldPosition : TEXCOORD2;
float4 Position : SV_Position;
};
@@ -10,21 +12,25 @@ Output main(uint vertexIndex : SV_VertexID)
{
Output output;
// Retrieve the buffer using SM6.6 bindless syntax
// We use index 0 as the sample app doesn't pass push constants yet.
uint bufferIndex = 0;
ByteAddressBuffer buffer = ResourceDescriptorHeap[bufferIndex];
ByteAddressBuffer buffer = ResourceDescriptorHeap[BufferIndex];
// Read position from buffer (Index * stride)
// Stride = 2 float (pos) + 4 float (color) = 6 * 4 = 24 bytes ?
// Let's assume just position 2D (8 bytes) + Color (16 bytes)
uint stride = 24;
uint stride = 40;
uint offset = vertexIndex * stride;
float2 pos = asfloat(buffer.Load2(offset));
float4 col = asfloat(buffer.Load4(offset + 8));
float3 pos = asfloat(buffer.Load3(offset));
float3 normal = asfloat(buffer.Load3(offset + 12));
float4 col = asfloat(buffer.Load4(offset + 24));
output.Position = float4(pos, 0.0f, 1.0f);
output.Color = col;
// Fetch Model Matrix
StructuredBuffer<TransformData> transformsBuffer = ResourceDescriptorHeap[TransformsBufferIndex];
float4x4 Model = transformsBuffer[MeshIndex].Model;
float4 worldPos = mul(Model, float4(pos, 1.0f));
output.Position = mul(ViewProjection, worldPos);
output.Color = col * MeshAlbedo;
output.WorldPosition = worldPos.xyz;
float3 worldNormal = mul((float3x3)Model, normal);
output.WorldNormal = worldNormal;
return output;
}
+3
View File
@@ -3,4 +3,7 @@
<LLVMInstallDir>C:\Program Files\LLVM</LLVMInstallDir>
<LLVMToolsVersion>19.1.0</LLVMToolsVersion>
</PropertyGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)Natvis\*.natvis" />
</ItemGroup>
</Project>
+1 -1
-2
View File
@@ -1,2 +0,0 @@
.CPPVersion = ' /std:c++20'
.CPPCompilerWarningOptions = ' -Wno-c++20-compat'
-3
View File
@@ -1,3 +0,0 @@
#if __WINDOWS__
#include "Windows/Clang.bff"
#endif
-221
View File
@@ -1,221 +0,0 @@
// Clang 19.x.x
//------------------------------------------------------------------------------
//
// Detect Clang
//
// We search in the following locations, in order of preference:
// 1) Vendorized in External (side by side with this bff)
// 2) Part of a Visual Studio installation (Program Files)
// 3) Default install location
//
#include "../../CPP/CPPHelper.bff"
#if file_exists( "19.1.5/bin/clang-cl.exe" )
// Vendorized
.Clang19_BasePath = '$_CURRENT_BFF_DIR_$/19.1.5'
.Clang19_Version = '19.1.5'
#else
#if file_exists( "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/LLVM/x64/bin/clang-cl.exe" )
// Installed with VS2022
.Clang19_BasePath = 'C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/LLVM/x64'
.Clang19_Version = '19.x.x'
#else
#if file_exists( "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Tools/LLVM/x64/bin/clang-cl.exe" )
// Installed with VS2022
.Clang19_BasePath = 'C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Tools/LLVM/x64'
.Clang19_Version = '19.x.x'
#else
#if file_exists( "C:/Program Files/LLVM/bin/clang-cl.exe" )
// Default Install
.Clang19_BasePath = 'C:/Program Files/LLVM'
.Clang19_Version = '19.x.x'
#else
//
// Failed
//
Print( '----------------------------------------------------------------------' )
Print( '- Unable to auto-detect Clang - please specify installation manually -' )
Print( '----------------------------------------------------------------------' )
.Clang19_BasePath = .Set_Path_Here // <-- Set path here
.Clang19_Version = .Set_Version_Here // <-- Set version here
#endif
#endif
#endif
#endif
// Print details of used version. TODO:C Move to Clang.bff
Print( 'Using Clang $Clang19_Version$ from $Clang19_BasePath$' )
// Compiler
//------------------------------------------------------------------------------
Compiler( 'Compiler-Clang19' )
{
.Root = '$Clang19_BasePath$'
.ExtraFiles = {
'$Root$/bin/concrt140.dll'
'$Root$/bin/msvcp140.dll'
'$Root$/bin/vcruntime140.dll'
'$Root$/bin/vcruntime140_1.dll'
}
.Executable = '$Root$\bin\clang-cl.exe'
// Allow tests to activate some experimental behavior
#if ENABLE_RELATIVE_PATHS
.UseRelativePaths_Experimental = true
#endif
#if ENABLE_SOURCE_MAPPING
.SourceMapping_Experimental = '/fastbuild-test-mapping'
#endif
}
// Compiler
//------------------------------------------------------------------------------
Compiler( 'Compiler-Clang19-NonCL' )
{
.Root = '$Clang19_BasePath$'
.ExtraFiles = {
'$Root$/bin/msvcp140.dll'
'$Root$/bin/vcruntime140.dll'
}
.Executable = '$Root$\bin\clang.exe'
// Allow tests to activate some experimental behavior
#if ENABLE_RELATIVE_PATHS
.UseRelativePaths_Experimental = true
#endif
#if ENABLE_SOURCE_MAPPING
.SourceMapping_Experimental = '/fastbuild-test-mapping'
#endif
}
// ToolChain
//------------------------------------------------------------------------------
.ToolChain_Clang_Windows_Common =
[
// Clang for Windows relies on the VS being present:
// - crt headers
// - crt libs/dlls
Using( .ToolChain_VS_Windows_X64 )
.Platform = 'x64Clang'
// Librarian
.Librarian = '$Clang19_BasePath$\bin\llvm-ar.exe'
.LibrarianOptions = 'rc "%2" "%1"' // NOTE: output must come first
// = '/c echo %1 > %2' // Debug options
// Linker
.Linker = '$Clang19_BasePath$\bin\lld-link.exe'
.LinkerOptions = '/NODEFAULTLIB /WX /NOLOGO /INCREMENTAL:NO /OUT:"%2" "%1" /DEBUG'
+ .VSLibPaths
// Compiler Warnings
.CommonCompilerWarningOptions // Enable warnings
= ' -Wall -Wextra -Weverything' // All warnings
+ ' -Werror -Wfatal-errors' // Warnings as fatal errors
// Warnings that are not useful
+ ' -Wno-#pragma-messages' // warning : %s [-W#pragma-messages]
+ ' -Wno-c++98-compat-pedantic' // variadic macros are incompatible with C++98
+ ' -Wno-exit-time-destructors' // declaration requires an exit-time destructor
+ ' -Wno-global-constructors' // declaration requires a global destructor
+ ' -Wno-invalid-offsetof' // we get the offset of members in non-POD types
+ ' -Wno-missing-prototypes' // no previous prototype for function '%s'
+ ' -Wno-missing-variable-declarations' // no previous extern declaration for non-static variable '%s'
+ ' -Wno-gnu-line-marker' // Clang complains about directives its own preprocessor generated
+ ' -Wno-switch-enum' // Allow the use of "default" labels (we keep -Wswitch to ensure all cases are handled)
+ ' -Wno-switch-default' // Don't require default: when all cases are already handled
+ .CPPCompilerWarningOptions
// Warnings that fire but might be best to be fixed
+ ' -Wno-anon-enum-enum-conversion' // conversions between unrelated anonymous enums
+ ' -Wno-cast-function-type-strict' // converts to incompatible function type
+ ' -Wno-cast-qual' // cast from 'const %s *' to '%s *' drops const qualifier
+ ' -Wno-deprecated-copy-dtor' // definition of implicit copy constructor for '%s' is deprecated because it has a user-declared destructor
+ ' -Wno-missing-noreturn' // function '%s' could be declared with attribute 'noreturn'
+ ' -Wno-old-style-cast' // use of old-style cast
+ ' -Wno-unsafe-buffer-usage' // unsafe pointer arithmetic
// File Extensions
.LibExtension = '.a'
.ExeExtension = '.exe'
]
// ToolChain
//------------------------------------------------------------------------------
.ToolChain_Clang_Windows =
[
Using( .ToolChain_Clang_Windows_Common )
// Compiler Options
.Compiler = 'Compiler-Clang19'
.CommonCompilerOptions = ' -c' // Compile only
+ ' /Z7' // Include debug info
+ ' -D_CRT_SECURE_NO_WARNINGS'
// Unicode
+ ' /utf-8'
+ ' /D "_UNICODE" /D "UNICODE"'
// Include paths
+ ' -I"./"'
+ .VSIncludePaths_ClangCl
// x64
+ ' -m64'
+ ' --target=x86_64-windows-msvc'
// No RTTI
+ ' /GR-'
// No Exceptions
+ ' /EHs-c-'
// Warnings
+ .CommonCompilerWarningOptions
.CompilerOptions = ' /TP -o"%2" "%1" $CommonCompilerOptions$'
+ .CPPVersion
.CompilerOptionsC = ' /TC -o"%2" "%1" $CommonCompilerOptions$'
.PCHOptions = ' /TP $CommonCompilerOptions$ "%1" /Fo"%3" /Fp"%2" /Yc"PrecompiledHeader.h"'
+ .CPPVersion
]
// ToolChain
//------------------------------------------------------------------------------
.ToolChain_ClangNonCL_Windows =
[
Using( .ToolChain_Clang_Windows_Common )
// Compiler Options
.Compiler = 'Compiler-Clang19-NonCL'
.CommonCompilerOptions = ' -c' // Compile only
+ ' -g' // Include debug info
+ ' -D_CRT_SECURE_NO_WARNINGS'
// Unicode
+ ' /utf-8'
+ ' /D "_UNICODE" /D "UNICODE"'
// Include paths
+ ' "-I./"'
// x64
+ ' -m64'
+ ' --target=x86_64-windows-msvc'
// No RTTI
+ ' -fno-rtti'
// No Exceptions
+ ' -fno-exceptions'
// Warnings
+ .CommonCompilerWarningOptions
.CompilerOptions = ' -x c++ -o"%2" "%1" $CommonCompilerOptions$'
+ .CPPVersion
.CompilerOptionsC = ' -x c -o"%2" "%1" $CommonCompilerOptions$'
]
//------------------------------------------------------------------------------
-326
View File
@@ -1,326 +0,0 @@
// Visual Studio 2022
//------------------------------------------------------------------------------
//
// Detect VS2022
//
// We search in the following locations, in order of preference:
// 1) Vendorized in External (side by side with this bff)
// 2) Specified via environment variables (VS Command Prompt)
// 3) Part of a Visual Studio installation (Program Files)
//
#include "../CPP/CPPHelper.bff"
#if file_exists( "2022/Community/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe" )
//
// Use vendorized toolchain - v17.14.16
//
.VS2022_BasePath = '$_CURRENT_BFF_DIR_$/2022/Community/VC'
.VS2022_Version = '14.44.35207'
.VS2022_MSC_VER = '1944'
#define MSVC_DYNAMIC_DEOPT_AVAILABLE
#else
//
// Use environment variable if available (VS Command Prompt)
//
#if exists( VS170COMNTOOLS )
#import VCINSTALLDIR
#import VCToolsVersion
.VS2022_BasePath = .VCINSTALLDIR
.VS2022_Version = .VCToolsVersion
.VS2022_MSC_VER = '1944' // NOTE: This cannot be detected and may be incorrect
#else
//
// Use Visual Studio installation
//
#if file_exists( "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe" )
// v17.14.9
.VS2022_BasePath = 'C:/Program Files/Microsoft Visual Studio/2022/Community/VC'
.VS2022_Version = '14.44.35207'
.VS2022_MSC_VER = '1944'
#define MSVC_DYNAMIC_DEOPT_AVAILABLE
#else
//
// Use Visual Studio 2022 Enterprise installation (used by GitHub Actions)
//
#if file_exists( "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe" )
// v17.14.9
.VS2022_BasePath = 'C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC'
.VS2022_Version = '14.44.35207'
.VS2022_MSC_VER = '1944'
#define MSVC_DYNAMIC_DEOPT_AVAILABLE
#else
//
// Failed
//
Print( '-----------------------------------------------------------------------' )
Print( '- Unable to auto-detect VS2022 - please specify installation manually -' )
Print( '-----------------------------------------------------------------------' )
.VS2022_BasePath = .Set_Path_Here // <-- Set path here
.VS2022_Version = .Set_Version_Here // <-- Set version here
.VS2022_MSC_VER = .Set_MSC_VER_Here // <-- Set MSC_VER here
#endif
#endif
#endif
#endif
.VS2022_ToolchainPath = '$VS2022_BasePath$/Tools/MSVC/$VS2022_Version$'
// X64 Compiler
//------------------------------------------------------------------------------
Compiler( 'Compiler-VS2022-x64' )
{
.Root = '$VS2022_ToolchainPath$/bin/Hostx64/x64'
.Executable = '$Root$/cl.exe'
.ExtraFiles = { '$Root$/c1.dll',
'$Root$/c1xx.dll',
'$Root$/c2.dll',
'$Root$/msobj140.dll',
'$Root$/mspdb140.dll',
'$Root$/mspdbcore.dll',
'$Root$/mspdbsrv.exe',
'$Root$/mspft140.dll',
'$Root$/msvcp140.dll',
'$Root$/msvcp140_atomic_wait.dll', // Required circa 16.8.3 (14.28.29333)
'$Root$/tbbmalloc.dll', // Required as of 16.2 (14.22.27905)
'$Root$/vcruntime140.dll',
'$Root$/vcruntime140_1.dll', // Required as of 16.5.1 (14.25.28610)
'$Root$/1033/clui.dll',
'$Root$/1033/mspft140ui.dll' // Localized messages for static analysis
}
#if MSVC_DYNAMIC_DEOPT_AVAILABLE
+ '$Root$/c2dd.dll' // Dynamic Deoptimization as of 17.14.0 (14.44.34918)
#endif
#if ENABLE_LIGHT_CACHE
.UseLightCache_Experimental = true
#endif
}
// ARM64 Compiler
//------------------------------------------------------------------------------
Compiler( 'Compiler-VS2022-ARM64' )
{
.Root = '$VS2022_ToolchainPath$/bin/Hostx64/arm64'
.Executable = '$Root$/cl.exe'
.ExtraFiles = { '$Root$/c1.dll',
'$Root$/c1xx.dll',
'$Root$/c2.dll',
'$Root$/msobj140.dll',
'$Root$/mspdb140.dll',
'$Root$/mspdbcore.dll',
'$Root$/mspdbsrv.exe',
'$Root$/mspft140.dll',
'$Root$/msvcp140.dll',
'$Root$/msvcp140_atomic_wait.dll', // Required circa 16.8.3 (14.28.29333)
'$Root$/tbbmalloc.dll', // Required as of 16.2 (14.22.27905)
'$Root$/vcruntime140.dll',
'$Root$/vcruntime140_1.dll', // Required as of 16.5.1 (14.25.28610)
'$Root$/1033/clui.dll',
'$Root$/1033/mspft140ui.dll' // Localized messages for static analysis
}
#if ENABLE_LIGHT_CACHE
.UseLightCache_Experimental = true
#endif
}
// Common Toolchain for Windows
//------------------------------------------------------------------------------
.Toolchain_VS2022_Windows =
[
// Paths
.VSIncludePaths = ' /external:W0 /external:I"$VS2022_ToolchainPath$/include/"'
.VSIncludePaths_ClangCl = ' /imsvc "$VS2022_ToolchainPath$/include/"'
.VCPackagesPath = '$VS2022_BasePath$/../Common7/IDE/VC/vcpackages'
// Compiler
.CommonCompilerOptions = ' /Z7 /nologo /c'
+ ' /Zc:inline' // Remove unreferenced COMDATs at compile time (VS2013.2+)
+ ' /Zc:strictStrings' // Require const only usage of string literals (VS2013+)
+ ' /fp:fast'
+ ' /diagnostics:caret' // Use improved warning/error messages
+ .CPPVersion
+ ' /permissive-' // Require conformant code
+ ' /GR-' // No RTTI
+ ' /EHs-c-' // No exceptions
+ ' -D_CRT_SECURE_NO_WARNINGS'
+ ' /utf-8'
+ ' /D "_UNICODE" /D "UNICODE"'
// Warnings
+ ' /Wall' // Enable all warnings (we'll disable those that are not useful)
+ ' /WX' // Warnings as errors
// These warnings need further investigation
+ ' /wd5267' // definition of implicit copy constructor for '%s' is deprecated because it has a user-provided destructor
// These warnings are not useful
+ ' /wd4061' // enumerator '%s' in switch of enum '%s' is not explicitly handled by a case label
+ ' /wd4505' // unreferenced function with internal linkage has been removed
+ ' /wd4514' // '%s': unreferenced inline function has been removed
+ ' /wd4577' // 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify /EHsc
+ ' /wd4625' // '%s' : copy constructor was implicitly defined as deleted
+ ' /wd4710' // '%s': function not inlined
+ ' /wd4711' // function '%s' selected for automatic inline expansion
+ ' /wd4746' // volatile access of '<expression>' is subject to /volatile:<iso|ms> setting; consider using __iso_volatile_load/store intrinsic functions
+ ' /wd4820' // '%s': '%u' bytes padding added after data member '%s'
+ ' /wd5045' // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
+ ' /wd5220' // '%s': a non-static data member with a volatile qualified type no longer implies that compiler generated copy/move constructors and copy/move assignment operators are not trivial
+ ' /wd5245' // 'function': unreferenced function with internal linkage has been removed
// Include Paths
+ ' /I"./"'
+ .VSIncludePaths
.CompilerOptions = ' $CommonCompilerOptions$ "%1" /Fo"%2"'
.CompilerOptionsC = ' $CommonCompilerOptions$ "%1" /Fo"%2"'
.CompilerOptionsDeoptimized = ' $CommonCompilerOptions$ "%1" /Fo"%2"'
.PCHOptions = ' $CommonCompilerOptions$ "%1" /Fo"%3" /Fp"%2" /Yc"PrecompiledHeader.h"'
// Librarian
.LibrarianOptions = '/NODEFAULTLIB /WX /NOLOGO /OUT:"%2" "%1"'
// Linker
.LinkerOptions = '/NODEFAULTLIB /WX /NOLOGO /INCREMENTAL:NO /OUT:"%2" "%1" /DEBUG'
//CRT
.CRTLibs_Static = ' LIBCMT.LIB'
+ ' libvcruntime.lib'
+ ' libucrt.lib'
.CRTLibs_Dynamic = ' msvcrt.lib'
+ ' vcruntime.lib'
+ ' ucrt.lib'
+ ' msvcprt.lib'
.CRTLibs_DynamicDebug = ' msvcrtd.lib'
+ ' vcruntimed.lib'
+ ' ucrtd.lib'
+ ' msvcprtd.lib'
// File Extensions
.LibExtension = '.lib'
.ExeExtension = '.exe'
]
// X64 ToolChain for Windows
//------------------------------------------------------------------------------
.ToolChain_VS2022_Windows_X64 =
[
Using( .Toolchain_VS2022_Windows )
.Platform = 'x64'
// Paths
.VSLibPaths = ' /LIBPATH:"$VS2022_ToolchainPath$/lib/x64"'
// Compiler
.Compiler = 'Compiler-VS2022-X64'
// Librarian
.Librarian = '$VS2022_ToolchainPath$/bin/Hostx64/x64/lib.exe'
// Linker
.Linker = '$VS2022_ToolchainPath$/bin/Hostx64/x64/link.exe'
.LinkerOptions + .VSLibPaths
]
.ToolChain_VS2022_Windows_ARM64 =
[
Using( .Toolchain_VS2022_Windows )
.Platform = 'ARM64'
// Paths
.VSLibPaths = ' /LIBPATH:"$VS2022_ToolchainPath$/lib/arm64"'
// Compiler
.Compiler = 'Compiler-VS2022-ARM64'
// Librarian
.Librarian = '$VS2022_ToolchainPath$/bin/Hostx64/arm64/lib.exe'
// Linker
.Linker = '$VS2022_ToolchainPath$/bin/Hostx64/arm64/link.exe'
.LinkerOptions + .VSLibPaths
]
// PATH environment variable
//------------------------------------------------------------------------------
.VS_2022_PATH = '$VS2022_ToolchainPath$/Hostx64/x64'
// Platform Toolset for Project Generation
//------------------------------------------------------------------------------
.VS_2022_PlatformToolset = 'v143'
// _MSC_VER
//------------------------------------------------------------------------------
// VisualStudioSettings
//------------------------------------------------------------------------------
.VisualStudioSettings_VS2022 =
[
.VS_Version = .VS2022_Version
.VS_Version_HumanReadable = '2022'
.VS_SolutionVersion = '17'
.MSC_VER = .VS2022_MSC_VER
.VS_ToolchainPath = .VS2022_ToolchainPath
.ToolChain_VS_Windows_X64 = .ToolChain_VS2022_Windows_X64
.ToolChain_VS_Windows_ARM64 = .ToolChain_VS2022_Windows_ARM64
.VS_PATH = .VS_2022_PATH
.VS_PlatformToolset = .VS_2022_PlatformToolset
// Enable and specify analysis engine
.StaticAnalysisOptions = ' /analyze'
+ ' /analyze:plugin"$VS2022_ToolchainPath$\bin\HostX64\x64\EspXEngine.dll"'
+ ' /analyze:external-' // Disable analysis of external headers
// To investigate: These warnings might be useful and should be checked
+ ' /wd26400' // Do not assign the result of an allocation or a function call with an owner<T> return value to a raw pointer, use owner<T> instead (i.11).
+ ' /wd26401' // Do not delete a raw pointer that is not an owner<T> (i.11).
+ ' /wd26430' // Symbol '%s' is not tested for nullness on all paths (f.23).: Lines: ...
+ ' /wd26432' // If you define or delete any default operation in the type '%s', define or delete them all (c.21).
+ ' /wd26436' // The type '%s' with a virtual function needs either public virtual or protected non-virtual destructor (c.35).
+ ' /wd26438' // Avoid 'goto' (es.76).
+ ' /wd26451' // Arithmetic overflow: Using operator '*' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '*' to avoid overflow (io.2).
+ ' /wd26460' // The reference argument '%s' for function '%s' can be marked as const (con.3).
+ ' /wd26475' // Do not use function style C-casts (es.49).
+ ' /wd26476' // Expression/symbol '%s' uses a naked union '%s' with multiple type pointers: Use variant instead (type.7).
+ ' /wd26491' // Don't use static_cast downcasts (type.2).
+ ' /wd26494' // Variable '%s' is uninitialized. Always initialize an object (type.5).
+ ' /wd26495' // Variable '%s' is uninitialized. Always initialize a member variable (type.6).
+ ' /wd26497' // The function '%s' could be marked constexpr if compile-time evaluation is desired (f.4).
+ ' /wd26814' // The const variable '%s' can be computed at compile-time. Consider using constexpr (con.5).
// These warnings are not useful. They either:
// a) Are about things we want to do or have no viable alternative
// b) Are nonsensical, undesirable or impractical
+ ' /wd26402' // Return a scoped object instead of a heap-allocated if it has a move constructor (r.3).
+ ' /wd26409' // Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11).
+ ' /wd26426' // Global initializer calls a non-constexpr function '%s' (i.22).
+ ' /wd26429' // Symbol '%s' is never tested for nullness, it can be marked as not_null (f.23).
+ ' /wd26435' // Function '%s' should specify exactly one of 'virtual', 'override', or 'final' (c.128).
+ ' /wd26439' // This kind of function may not throw. Declare it 'noexcept' (f.6).
+ ' /wd26440' // Function '%s' can be declared 'noexcept' (f.6).
+ ' /wd26446' // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).
+ ' /wd26447' // The function is declared 'noexcept' but calls function '%s' which may throw exceptions (f.6).
+ ' /wd26455' // Default constructor may not throw. Declare it 'noexcept' (f.6).
+ ' /wd26457' // (void) should not be used to ignore return values, use 'std::ignore =' instead (es.48).
+ ' /wd26461' // The pointer argument '%s' for function '%s' can be marked as a pointer to const (con.3).
+ ' /wd26467' // Converting from floating point to unsigned integral types results in non-portable code if the double/float has a negative value. Use gsl::narrow_cast or gsl::narrow instead to guard against undefined behavior and potential data loss (es.46).
+ ' /wd26472' // Don't use a static_cast for arithmetic conversions. Use brace initialization, gsl::narrow_cast or gsl::narrow (type.1).
+ ' /wd26481' // Don't use pointer arithmetic. Use span instead (bounds.1).
+ ' /wd26482' // Only index into arrays using constant expressions (bounds.2).'
+ ' /wd26485' // Expression '%s': No array to pointer decay (bounds.3).
+ ' /wd26490' // Don't use reinterpret_cast (type.1).
+ ' /wd26492' // Don't use const_cast to cast away const or volatile (type.3).
+ ' /wd26493' // Don't use C-style casts (type.4).
+ ' /wd26826' // Don't use C-style variable arguments (f.55).
+ ' /wd26831' // Allocation size might be the result of a numerical overflow.
]
//------------------------------------------------------------------------------
-20
View File
@@ -1,20 +0,0 @@
// VisualStudio
//------------------------------------------------------------------------------
#if __WINDOWS__
// Select desired Visual Studio version
//------------------------------------------------------------------------------
#define USING_VS2022
// Activate
//------------------------------------------------------------------------------
.VisualStudioSettings = []
#if USING_VS2022
#include "VS2022.bff"
.VisualStudioSettings = .VisualStudioSettings_VS2022
#endif
Using( .VisualStudioSettings )
Print( 'Using VS$VS_Version_HumanReadable$ (v$VS_Version$) from $VS_ToolchainPath$' )
//------------------------------------------------------------------------------
#endif
-16
View File
@@ -1,16 +0,0 @@
// Windows
//------------------------------------------------------------------------------
#if __WINDOWS__
// The Windows 10 SDK is used regardless of which OS version is being targetted
#define USING_WINDOWS10_SDK
// Activate
//------------------------------------------------------------------------------
#if USING_WINDOWS10_SDK
#include "Windows10SDK.bff"
Alias( 'ResourceCompiler' ) { .Targets = 'ResourceCompiler-Windows10' }
#endif
//------------------------------------------------------------------------------
#endif
-121
View File
@@ -1,121 +0,0 @@
// Windows 10 SDK
//------------------------------------------------------------------------------
//
// Detect Windows 10 SDK
//
// We search in the following locations, in order of preference:
// 1) Vendorized in External (side by side with this bff)
// 2) Part of a Windows 10 installation (Program Files)
//
#if file_exists( "10\bin\10.0.26100.0\x64\RC.exe" )
//
// Use vendorized toolchain
//
.Windows10_SDKBasePath = '$_CURRENT_BFF_DIR_$\10'
.Windows10_SDKVersion = '10.0.26100.0'
#else
//
// Use Windows10SDK installation
//
#if file_exists( "C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\RC.exe" )
.Windows10_SDKBasePath = 'C:\Program Files (x86)\Windows Kits\10'
.Windows10_SDKVersion = '10.0.26100.0'
#else
//
// Failed
//
Print( '-----------------------------------------------------------------------' )
Print( '- Unable to auto-detect Windows10SDK - please specify installation manually -' )
Print( '-----------------------------------------------------------------------' )
.Windows10_SDKBasePath = .Set_Path_Here // <-- Set path here
.Windows10_SDKVersion = .Set_Version_Here // <-- Set version here
#endif
#endif
// Defines
//------------------------------------------------------------------------------
.WindowsSDK_Defines = ' -DWIN32_LEAN_AND_MEAN'
+ ' -DWIN32'
+ ' -D_WIN32'
+ ' -D__WINDOWS__'
+ ' -D_HAS_EXCEPTIONS=0'
// Windows 10 SDK
//------------------------------------------------------------------------------
.Windows10_SDK =
[
#if USING_VS2022
.WindowsSDK_IncludePaths = ' "/external:I$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/ucrt"'
+ ' "/external:I$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/um"'
+ ' "/external:I$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/shared"'
#else
.WindowsSDK_IncludePaths = ' "-I$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/ucrt"'
+ ' "-I$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/um"'
+ ' "-I$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/shared"'
#endif
.WindowsSDK_IncludePaths_RC = ' "-I$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/ucrt"'
+ ' "-I$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/um"'
+ ' "-I$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/shared"'
.WindowsSDK_WinRTAssemblyPath = '$Windows10_SDKBasePath$/UnionMetadata/$Windows10_SDKVersion$/'
.CommonCompilerOptions = .WindowsSDK_IncludePaths
+ .WindowsSDK_Defines
+ ' -DWIN64'
.CompilerOptions = .CommonCompilerOptions
.CompilerOptionsC = .CommonCompilerOptions
.CompilerOptionsDeoptimized = .CommonCompilerOptions
.PCHOptions = .CommonCompilerOptions
]
.Windows10_SDK_X64 =
[
Using( .Windows10_SDK )
.LinkerOptions = ' /LIBPATH:"$Windows10_SDKBasePath$/Lib/$Windows10_SDKVersion$/um/x64"'
+ ' /LIBPATH:"$Windows10_SDKBasePath$/Lib/$Windows10_SDKVersion$/ucrt/x64"'
+ ' /MACHINE:X64'
]
.Windows10_SDK_ARM64 =
[
Using( .Windows10_SDK )
.LinkerOptions = ' /LIBPATH:"$Windows10_SDKBasePath$/Lib/$Windows10_SDKVersion$/um/arm64"'
+ ' /LIBPATH:"$Windows10_SDKBasePath$/Lib/$Windows10_SDKVersion$/ucrt/arm64"'
+ ' /MACHINE:ARM64'
]
.Windows10_SDK_X64_ClangCl =
[
// When using clang-cl, we can use these include paths to suppress compile errors
// inside system headers (which are full of problems)
.WindowsSDK_IncludePaths = ' /imsvc "$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/ucrt"'
+ ' /imsvc "$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/um"'
+ ' /imsvc "$Windows10_SDKBasePath$/Include/$Windows10_SDKVersion$/shared"'
.WindowsSDK_WinRTAssemblyPath = '$Windows10_SDKBasePath$/UnionMetadata/$Windows10_SDKVersion$/'
.CommonCompilerOptions = .WindowsSDK_IncludePaths
+ .WindowsSDK_Defines
+ ' -DWIN64'
.CompilerOptions = .CommonCompilerOptions
.CompilerOptionsC = .CommonCompilerOptions
.CompilerOptionsDeoptimized = .CommonCompilerOptions
.PCHOptions = .CommonCompilerOptions
.LinkerOptions = ' /LIBPATH:"$Windows10_SDKBasePath$/Lib/$Windows10_SDKVersion$/um/x64"'
+ ' /LIBPATH:"$Windows10_SDKBasePath$/Lib/$Windows10_SDKVersion$/ucrt/x64"'
+ ' /MACHINE:X64'
]
Compiler( 'ResourceCompiler-Windows10' )
{
.Executable = '$Windows10_SDKBasePath$/Bin/$Windows10_SDKVersion$/x64/RC.exe'
.CompilerFamily = 'custom'
}
// PATH environment variable
//------------------------------------------------------------------------------
.WINDOWS_SDK_2019_PATH = '$Windows10_SDKBasePath$/bin/$Windows10_SDKVersion$/x64'
//------------------------------------------------------------------------------
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include <Juliet.h>
#define kPlayCamera ((index_t)0)
#define kDebugCamera ((index_t)1)
+163
View File
@@ -0,0 +1,163 @@
#include <Controller/DebugCameraController.h>
#include <Controller/ControllerUtils.h>
#include <Core/Common/CoreUtils.h>
#include <Core/HAL/Keyboard/Keyboard.h>
#include <Core/HAL/Mouse/Mouse.h>
#include <Core/Memory/MemoryArena.h>
#include <Graphics/Camera.h>
#include <dcimgui.h>
#include <math.h>
global bool8 gIsDebugCameraActive = false;
global index_t gPreviousCameraIndex = 0;
global float gPitch = 0.0f;
global float gYaw = 0.0f;
global bool8 gFirstUpdate = true;
global bool8 gIsFpsModeActive = false;
global bool8 gWasRightMouseButtonDown = false;
void ActivateDebugController(void)
{
Assert(gIsDebugCameraActive == false);
Camera* currentCam = GetCurrentCamera();
gPreviousCameraIndex = currentCam->Index;
SetCurrentCamera(kDebugCamera);
gIsDebugCameraActive = true;
gFirstUpdate = true;
gIsFpsModeActive = false;
gWasRightMouseButtonDown = false;
}
void DeactivateDebugController(void)
{
Assert(gIsDebugCameraActive);
gIsDebugCameraActive = false;
SetCurrentCamera(gPreviousCameraIndex);
}
bool8 IsDebugControllerActive(void)
{
return gIsDebugCameraActive;
}
void UpdateDebugController(float dt)
{
Camera* currentCam = GetCurrentCamera();
if (gFirstUpdate)
{
Vector3 origin = { 0.0f, 0.0f, 0.0f };
Vector3 dir = Vector3Sub(origin, currentCam->Position);
dir = Normalize(dir);
gPitch = asinf(dir.z);
gYaw = atan2f(dir.y, dir.x);
gFirstUpdate = false;
Vector3 forward = { cosf(gPitch) * cosf(gYaw), cosf(gPitch) * sinf(gYaw), sinf(gPitch) };
Vector3 worldUp = { 0.0f, 0.0f, 1.0f };
Vector3 right = Normalize(Cross(worldUp, forward));
currentCam->Target = Vector3Add(currentCam->Position, forward);
currentCam->Up = Cross(forward, right);
}
bool8 isRightMouseButtonDown = IsMouseButtonDown(MouseButton_Right);
if (isRightMouseButtonDown && !gWasRightMouseButtonDown)
{
gIsFpsModeActive = !gIsFpsModeActive;
}
gWasRightMouseButtonDown = isRightMouseButtonDown;
if (gIsFpsModeActive)
{
MousePosition mouseDelta = GetMouseDelta();
float sensitivity = 0.005f;
gYaw += (float)mouseDelta.X * sensitivity;
gPitch -= (float)mouseDelta.Y * sensitivity;
gPitch = Min(gPitch, 1.5f);
gPitch = Max(gPitch, -1.5f);
if (IsKeyDown(ScanCode_Q))
{
gYaw -= 2.0f * dt;
}
if (IsKeyDown(ScanCode_E))
{
gYaw += 2.0f * dt;
}
Vector3 forward = { cosf(gPitch) * cosf(gYaw), cosf(gPitch) * sinf(gYaw), sinf(gPitch) };
Vector3 worldUp = { 0.0f, 0.0f, 1.0f };
Vector3 right = Normalize(Cross(worldUp, forward));
Vector3 defaultUp = Cross(forward, right);
const float kMovementPerFrame = 10.f; // 10m/s
float speedPerFrame = kMovementPerFrame;
if (IsKeyDown(ScanCode_LeftShift))
{
speedPerFrame *= 10.f; // 100m/s
}
if (IsKeyDown(ScanCode_W))
{
currentCam->Position = Vector3Add(currentCam->Position, Vector3Scale(forward, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_S))
{
currentCam->Position = Vector3Sub(currentCam->Position, Vector3Scale(forward, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_D))
{
currentCam->Position = Vector3Add(currentCam->Position, Vector3Scale(right, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_A))
{
currentCam->Position = Vector3Sub(currentCam->Position, Vector3Scale(right, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_Space))
{
currentCam->Position = Vector3Add(currentCam->Position, Vector3Scale(defaultUp, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_LeftControl))
{
currentCam->Position = Vector3Sub(currentCam->Position, Vector3Scale(defaultUp, speedPerFrame * dt));
}
currentCam->Target = Vector3Add(currentCam->Position, forward);
currentCam->Up = defaultUp;
}
}
#if JULIET_DEBUG
void RenderImGuiDebugController(float dt)
{
Camera* currentCam = GetCurrentCamera();
ImGui_Text("Delta time: %f", (double)dt);
ImGui_Text("Camera:");
ImGui_Indent();
ImGui_BulletText("Index: %zu", (size_t)currentCam->Index);
ImGui_BulletText("Position: (%.3f, %.3f, %.3f)", (double)currentCam->Position.x, (double)currentCam->Position.y,
(double)currentCam->Position.z);
ImGui_BulletText("Target: (%.3f, %.3f, %.3f)", (double)currentCam->Target.x, (double)currentCam->Target.y,
(double)currentCam->Target.z);
ImGui_BulletText("Up: (%.3f, %.3f, %.3f)", (double)currentCam->Up.x, (double)currentCam->Up.y,
(double)currentCam->Up.z);
ImGui_BulletText("FOV: %.3f rad", (double)currentCam->FOV);
ImGui_BulletText("AspectRatio: %.3f", (double)currentCam->AspectRatio);
ImGui_BulletText("NearPlane: %.3f", (double)currentCam->NearPlane);
ImGui_BulletText("FarPlane: %.3f", (double)currentCam->FarPlane);
ImGui_BulletText("Pitch: %.3f, Yaw: %.3f", (double)gPitch, (double)gYaw);
ImGui_BulletText("FPS Mode: %s", gIsFpsModeActive ? "Active" : "Inactive");
ImGui_Unindent();
}
#endif
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <Juliet.h>
// TODO : Should support Debug/Profile/Release properly.
// I think debug profile should have this controller but not release
void ActivateDebugController(void);
void DeactivateDebugController(void);
bool8 IsDebugControllerActive(void);
void UpdateDebugController(float dt);
#if JULIET_DEBUG
void RenderImGuiDebugController(float dt);
#endif
+140
View File
@@ -0,0 +1,140 @@
#include <Data/World.h>
#include <Core/Common/CoreUtils.h>
#include <Core/Common/serialization.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/HAL/IO/IOStream.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Thread/ThreadContext.h>
#include <Entity/EntityManager.h>
#include <Graphics/MeshRenderer.h>
#ifdef JULIET_ENABLE_IMGUI
#include <dcimgui.h>
#endif
void InitWorld(World* world, Arena* arena)
{
Assert(world != NULL);
Assert(arena != NULL);
world->WorldArena = arena;
}
void ShutdownWorld(World* world)
{
Assert(world != NULL);
world->WorldArena = NULL;
}
void AddToWorld(World* world, Entity* entity)
{
(void)world;
(void)entity;
}
void RemoveWorldEntity(World* world, size_t index)
{
(void)world;
(void)index;
}
void SerializeWorld(Archive* ar, World* world, String filename)
{
(void)world;
Assert(ar != NULL);
Assert(IsValidString(filename));
EntityManager* entityManager = GetEntityManager();
if (ar->loading)
{
// Load
ByteBuffer fileBuffer = LoadFile(ar->arena, filename);
// TEST SERIALIZATION
ParsedArchive archive = tokenize_archive(ar->arena, fileBuffer);
#if JULIET_DEBUG
audit_unconsumed_properties(&archive, ConstString("World"));
#else
(void)archive;
#endif
}
else
{
// Save
IOStream* stream = IOFromFile(ar->arena, filename, WrapString("wb"));
index_t beginPos = ArenaPos(ar->arena);
// Headers
WorldFileHeader* header = ArenaPushStruct(ar->arena, WorldFileHeader);
header->Magic = kWorldMagic;
// TODO : Move to a one file per entity model
for (uint8 i = 0; i < ENTITY(Count); ++i)
{
typed_entity_array* type = &entityManager->by_type[i];
serialize_elem(ar, type->count);
if (type->count > 0 && type->array != NULL)
{
EntityTemplate* element = type->array;
uint8* rawElement = (uint8*)element;
size_t stride = element->base->derived_kind->size_of;
for (index_t idx = 0; idx < type->count; ++idx)
{
Entity* entity = ((EntityTemplate*)(rawElement + (idx * stride)))->base;
serialize_entity(ar, entity);
}
}
}
// Write
index_t endPos = ArenaPos(ar->arena);
ByteBuffer writeBuffer = { .Data = (Byte*)header, .Size = endPos - beginPos };
size_t written = IOWrite(stream, writeBuffer);
Assert(writeBuffer.Size == written);
IOClose(stream);
}
}
bool8 LoadWorld(World* world, String filename)
{
(void)world;
Assert(IsValidString(filename));
return true;
}
#ifdef JULIET_EDITOR
void RenderWorldEditorUI(World* world)
{
Assert(world != NULL);
if (ImGui_Begin("World Editor", NULL, ImGuiWindowFlags_None))
{
TempArena temp = scratch_begin(NULL, 0);
static char worldFilePath[256] = "/world.bin";
ImGui_InputText("World File", worldFilePath, sizeof(worldFilePath));
Path path = GetAssetPath(temp.Arena, AssetType_JAsset, WrapString(worldFilePath));
if (ImGui_Button("Save World"))
{
Archive data = { .arena = temp.Arena, .loading = false };
SerializeWorld(&data, world, path.FullPath);
}
ImGui_SameLine(0.0f, -1.0f);
if (ImGui_Button("Load World"))
{
Archive data = { .arena = temp.Arena, .loading = true };
SerializeWorld(&data, world, path.FullPath);
}
scratch_end(temp);
}
ImGui_End();
}
#endif
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include <Core/Common/String.h>
#include <Core/Container/Vector.h>
#include <Core/Memory/MemoryArena.h>
#include <Entity/Entity.h>
typedef struct Archive Archive;
typedef struct EntityManager EntityManager;
#pragma pack(push, 1)
typedef struct WorldFileHeader WorldFileHeader;
struct WorldFileHeader
{
uint32 Magic; // 0x444C574A ('JWLD' in little-endian)
};
#pragma pack(pop)
#define kWorldMagic ((uint32)0x444C574A) // 'JWLD'
#define kWorldVersion ((uint32)1)
typedef struct World World;
struct World
{
Arena* WorldArena;
EntityManager* EntityManager;
};
void InitWorld(World* world, Arena* arena);
void ShutdownWorld(World* world);
void AddToWorld(World* world, Entity* entity);
void RemoveWorldEntity(World* world, size_t index);
void SerializeWorld(Archive* data, World* world, String filename);
#if JULIET_EDITOR
void RenderWorldEditorUI(World* world);
#endif
+63
View File
@@ -0,0 +1,63 @@
#include <Debug/DebugTopBar.h>
#if JULIET_DEBUG
#include <game.h>
#include <dcimgui.h>
#include <stdio.h>
internal String GetGameModeName(GameMode gameMode)
{
String result = WrapString("Unknown ERROR");
if (gameMode == GameMode_Editor)
{
result = WrapString("Editor");
}
else if (gameMode == GameMode_Play)
{
result = WrapString("Play");
}
else if (gameMode == GameMode_Debug)
{
result = WrapString("Debug");
}
return result;
}
void DrawTopBar(void)
{
GameState* gameState = GetGameState();
ImVec2_c viewportPos = ImGui_GetMainViewportPos();
ImVec2_c viewportSize = ImGui_GetMainViewportSize();
ImGui_SetNextWindowPos(viewportPos);
ImVec2_c barSize = { viewportSize.x, 10.0f };
ImGui_SetNextWindowSize(barSize);
ImGuiWindowFlags_c flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoNav;
ImVec4_c bgCol = { 0.0f, 0.0f, 0.0f, 0.6f };
ImVec2_c padding = { 10.0f, 5.0f };
ImGui_PushStyleColor(ImGuiCol_WindowBg, bgCol);
ImGui_PushStyleVarVec2(ImGuiStyleVar_WindowPadding, padding);
if (ImGui_Begin("TopBarOverlay", NULL, flags))
{
ImGui_Text("Game Mode: %s", CStr(GetGameModeName(gameState->Mode)));
float framerate = ImGuiIO_GetFramerate();
char fps_text[32];
snprintf(fps_text, sizeof(fps_text), "FPS: %.1f (%.2f ms)", (double)framerate, (double)(1000.0f / framerate));
float text_width = ImGui_CalcTextSize(fps_text, NULL).x;
ImGui_SameLine(viewportSize.x - text_width - 15.0f, -1.0f); // 15px right padding
// Render FPS text
ImGui_Text("%s", fps_text);
}
ImGui_End();
ImGui_PopStyleVar(1);
ImGui_PopStyleColor(1);
}
#endif
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <Juliet.h>
#if JULIET_DEBUG
void DrawTopBar(void);
#endif
+115
View File
@@ -0,0 +1,115 @@
#include <Entity/Entity.h>
#include <Core/Common/serialization.h>
#include <Core/Memory/Utils.h>
#include <stdlib.h>
void serialize_entity(Archive* ar, Entity* entity)
{
Assert(ar != NULL);
Assert(entity != NULL);
// Entity fields
serialize_class(ar, ClassKind(Entity), entity);
// Derived fields
if (entity->derived_kind != NULL && entity->derived != NULL)
{
serialize_class(ar, entity->derived_kind, entity->derived);
}
}
internal void init_Entity(void* payload)
{
Assert(payload != NULL);
Entity* entity = (Entity*)payload;
MemZeroStruct(entity);
}
internal void serialize_Entity(Archive* ar, uint16 version, void* payload)
{
(void)version;
Assert(ar != NULL);
Assert(payload != NULL);
Entity* entity = (Entity*)payload;
SERIALIZE_U64(ar, id, &entity->ID);
SERIALIZE_VEC4(ar, position, &entity->position);
}
DEFINE_CLASS_VERSIONED(Entity, 1, NULL)
String format_entity_id(Arena* arena, EntityID entity_id)
{
Assert(arena != NULL);
// Format:
// U = UserID
// LC = Local Entity Counter
// UU_LCLCLCLCLCLCLC
uint8 user_id = get_user_id(entity_id);
uint64 counter = get_local_counter(entity_id);
const size_t kBufferSize = 18;
char* buffer = ArenaPushArray(arena, char, kBufferSize);
Assert(buffer != NULL);
juliet_snprintf(buffer, kBufferSize, "%02X_%014llX", (unsigned int)user_id, (unsigned long long)counter);
String result = { .Str = buffer, .Size = kBufferSize - 1 };
return result;
}
bool8 parse_entity_id(String str, EntityID* out_id)
{
Assert(out_id != NULL);
bool8 result = false;
*out_id = kInvalid_entity_id;
if (!(str.Size != 17 || str.Str == NULL || str.Str[2] != '_'))
{
char user_hex[3] = { str.Str[0], str.Str[1], '\0' };
char* end = user_hex;
uint8 user_id = (uint8)strtoul(user_hex, &end, 16);
if (end != user_hex && user_id != kInvalid_user_id)
{
char* counter_hex = str.Str + 3;
end = counter_hex;
uint64 counter = strtoull(counter_hex, &end, 16);
if (end != counter_hex && counter < kEntityID_user_id_mask)
{
*out_id = make_entity_id(user_id, counter);
result = true;
}
}
}
return result;
}
const Class* resolve_entity_class(uint8 kind, uint32 crc)
{
Assert(kind <= ENTITY(Count));
const Class* class_ptr = kEntity_type_class_ptr[kind];
Assert(class_ptr != NULL);
Assert(class_get_crc(class_ptr) == crc);
return class_ptr;
}
const Class* find_class_by_name(String name)
{
Assert(IsValidString(name));
const Class* result = NULL;
const uint32 name_crc = crc32_bytes(name.Str, name.Size);
for (uint8 kind = 0; kind < ENTITY(Count); ++kind)
{
const Class* class_ptr = resolve_entity_class(kind, name_crc);
if (class_ptr != NULL)
{
result = class_ptr;
break;
}
}
return result;
}
+85 -58
View File
@@ -1,73 +1,100 @@
#pragma once
#include <concepts>
#include <Core/Common/CoreUtils.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Math/Vector.h>
#include <Engine/Class.h>
#include <Entity/entity_common.h>
#include <Entity/EntityManager.h>
#include <type_traits>
// Add any new fields into the concept below
#define DECLARE_ENTITY() \
Entity* Base; \
static const Juliet::Class* Kind;
// Will register the class globally at launch
#define DEFINE_ENTITY(entity) \
constexpr Juliet::Class entityKind##entity(#entity, sizeof(#entity) / sizeof(char)); \
const Juliet::Class* entity::Kind = &entityKind##entity;
namespace Game
typedef struct Entity Entity;
struct Entity
{
using DerivedType = void*;
EntityID ID;
const Class* derived_kind;
DerivedType derived;
Vector4 position;
bool8 is_dirty;
};
DECLARE_CLASS(Entity)
struct Entity final
{
EntityID ID;
const Juliet::Class* Kind;
DerivedType Derived;
float X, Y;
};
// Can cast to this to have the offset of base for any entity
typedef struct EntityTemplate EntityTemplate;
struct EntityTemplate
{
DECLARE_ENTITY()
};
template <typename EntityType>
concept EntityConcept = requires(EntityType entity) {
requires std::same_as<decltype(entity.Kind), const Juliet::Class*>;
requires std::same_as<decltype(entity.Base), Entity*>;
};
static inline EntityID make_entity_id(uint8 user_id, uint64 local_counter)
{
Assert(user_id != kInvalid_user_id);
Assert((local_counter & kEntityID_user_id_mask) == kInvalid_entity_id);
template <typename EntityType>
requires EntityConcept<EntityType>
bool IsA(const Entity* entity)
{
return entity->Kind == EntityType::Kind;
}
EntityID result = ((EntityID)user_id << kEntityID_user_id_shift) | (local_counter & kEntityID_user_id_mask);
return result;
}
template <typename EntityType>
requires EntityConcept<EntityType>
EntityType* MakeEntity(EntityManager& manager, float x, float y)
{
auto* arena = Juliet::GetGameArena();
EntityType* result = Juliet::ArenaPushType<EntityType>(arena);
Entity* base = result->Base = Juliet::ArenaPushType<Entity>(arena);
base->X = x;
base->Y = y;
base->Derived = result;
base->Kind = EntityType::Kind;
static inline uint8 get_user_id(EntityID entity_id)
{
return (uint8)((entity_id & kEntityID_user_id_mask) >> kEntityID_user_id_shift);
}
RegisterEntity(manager, base);
static inline uint64 get_local_counter(EntityID entity_id)
{
return (uint64)entity_id & ~kEntityID_user_id_mask;
}
return result;
}
static inline bool8 is_entity_id_valid(EntityID entity_id)
{
bool8 result = (entity_id != kInvalid_entity_id);
return result;
}
template <typename EntityType>
requires EntityConcept<EntityType>
EntityType* DownCast(Entity* entity)
{
Assert(IsA<EntityType>(entity));
return static_cast<EntityType*>(entity->Derived);
}
static inline bool8 IsEntityA(const Entity* entity, const Class* kind)
{
Assert(entity != NULL);
return entity->derived_kind == kind;
}
} // namespace Game
#define IsEntityOfType(entity_ptr, EntityType) IsEntityA((entity_ptr), ClassKind(EntityType))
static inline void* make_entity_by_class(EntityManager* manager, const Class* kind, float x, float y, float z)
{
Assert(manager != NULL);
Assert(kind != NULL);
Entity* base_ptr = allocate_entity(manager, kind);
Assert(base_ptr != NULL);
base_ptr->ID = g_entity_manager_id++;
base_ptr->position.x = x;
base_ptr->position.y = y;
base_ptr->position.z = z;
base_ptr->position.w = 1.0f;
return base_ptr->derived;
}
#define MakeEntity(EntityType, manager_ptr, x, y, z) \
((EntityType*)make_entity_by_class((manager_ptr), ClassKind(EntityType), (x), (y), (z)))
static inline void* down_cast_entity(Entity* entity, const Class* kind)
{
Assert(entity != NULL);
Assert(IsEntityA(entity, kind));
return entity->derived;
}
#define DownCastEntity(EntityType, entity_ptr) ((EntityType*)down_cast_entity((entity_ptr), ClassKind(EntityType)))
void serialize_entity(Archive* ar, Entity* entity);
JULIET_API uint8 get_local_user_id(void);
JULIET_API String format_entity_id(Arena* arena, EntityID id);
JULIET_API bool8 parse_entity_id(String str, EntityID* outId);
const Class* find_class_by_name(String name);
const Class* resolve_entity_class(uint8 kind, uint32 crc);
+190
View File
@@ -0,0 +1,190 @@
#include <Entity/EntityManager.h>
#include <Core/Common/serialization.h>
#include <Core/Memory/Utils.h>
#include <Data/World.h>
#include <Engine/user.h>
#include <Entity/Entity.h>
#include <Entity/entity_types.h>
#include <game.h>
#include <Graphics/MeshRenderer.h>
EntityID g_entity_manager_id = 0;
void InitEntityManager(World* world)
{
Assert(world != NULL);
EntityManager* newManager = ArenaPushStruct(world->WorldArena, EntityManager);
newManager->next_local_counter = 1ULL;
world->EntityManager = newManager;
VectorArenaCreate(&newManager->Entities, Entity, world->WorldArena, 100000 JULIET_DEBUG_PARAM("Entities"));
ArenaParams by_type_params = { .ReserveSize = Kilobytes(1),
.CommitSize = Kilobytes(1),
.Name = "" JULIET_DEBUG_ONLY(, .DoNotReserveMore = true) };
for (uint8 i = 0; i < ENTITY(Count); ++i)
{
by_type_params.Name = kEntity_type_names[i];
newManager->by_type[i].arena = ArenaAllocate(by_type_params);
newManager->by_type[i].array = NULL;
newManager->by_type[i].count = 0;
}
}
void ShutdownEntityManager(void)
{
EntityManager* manager = GetEntityManager();
for (uint8 i = 0; i < ENTITY(Count); ++i)
{
ArenaRelease(manager->by_type[i].arena);
}
vector_arena_destroy(&manager->Entities);
}
EntityManager* GetEntityManager(void)
{
EntityManager* entityManager = GetGameState()->World->EntityManager;
Assert(entityManager != NULL);
return entityManager;
}
EntityID generate_next_entity_id(EntityManager* manager)
{
Assert(manager != NULL);
UserID user = get_local_user_id();
Assert(user != kInvalid_user_id);
Assert(manager->next_local_counter != kEntityID_user_id_mask);
EntityID new_id = make_entity_id(user, manager->next_local_counter);
manager->next_local_counter += 1ULL;
return new_id;
}
EntityTemplate* RegisterEntity(EntityManager* manager, Entity* base, DerivedType entity)
{
Assert(manager != NULL);
Assert(base != NULL);
base->ID = g_entity_manager_id++;
base->derived = entity;
VectorArenaPushPtr(Entity, &manager->Entities, base);
EntityTemplate* ptr = (EntityTemplate*)ArenaPushSize(
manager->by_type[base->derived_kind->kind].arena, base->derived_kind->size_of, base->derived_kind->alignment,
false JULIET_DEBUG_PARAM(kEntity_type_names[base->derived_kind->kind]));
MemCopy(ptr, entity, base->derived_kind->size_of);
manager->by_type[base->derived_kind->kind].count += 1;
ptr->base = VectorArenaBack(Entity, &manager->Entities);
if (manager->by_type[base->derived_kind->kind].array == NULL)
{
manager->by_type[base->derived_kind->kind].array = ptr;
}
return ptr;
}
Entity* allocate_entity(EntityManager* manager, const Class* derived_type_class)
{
Assert(manager != NULL);
Assert(derived_type_class != NULL);
Assert(derived_type_class->kind < ENTITY(Count));
Assert(derived_type_class->size_of >= sizeof(EntityTemplate));
Assert(derived_type_class->alignment > 0);
Assert(derived_type_class->initialize_fct != NULL);
Entity base_template = { 0 };
base_template.derived_kind = derived_type_class;
base_template.is_dirty = true;
VectorArenaPushPtr(Entity, &manager->Entities, &base_template);
Entity* base_ptr = VectorArenaBack(Entity, &manager->Entities);
Assert(base_ptr != NULL);
typed_entity_array* typed_array = &manager->by_type[derived_type_class->kind];
Assert(typed_array->arena != NULL);
void* derived = ArenaPushSize(typed_array->arena, derived_type_class->size_of, derived_type_class->alignment,
false JULIET_DEBUG_PARAM(kEntity_type_names[derived_type_class->kind]));
Assert(derived != NULL);
derived_type_class->initialize_fct(derived);
EntityTemplate* derived_template = (EntityTemplate*)derived;
base_ptr->derived = derived;
derived_template->base = base_ptr;
if (typed_array->array == NULL)
{
typed_array->array = derived_template;
}
typed_array->count += 1;
return base_ptr;
}
void UpdateEntityManager(EntityManager* manager)
{
Assert(manager != NULL);
// Todo : inert by definition dont move, but this is for test
typed_entity_array* by_type = &manager->by_type[ENTITY(Inert)];
for (index_t i = 0; i < by_type->count; ++i)
{
Inert* inert = ((Inert*)by_type->array) + i;
if (inert->MeshInstance != indexMax)
{
Matrix transform =
MatrixTranslation(inert->base->position.x, inert->base->position.y, inert->base->position.z);
SetMeshInstanceTransform(inert->MeshInstance, &transform);
}
}
}
Entity* deserialize_entity(Archive* ar, EntityManager* manager)
{
Assert(ar != NULL);
Assert(manager != NULL);
Assert(ar->loading);
String class_name = { 0 };
SERIALIZE_STR(ar, Class, &class_name);
const Class* class_ptr = find_class_by_name(class_name);
Assert(class_ptr != NULL);
Entity* entity_base = allocate_entity(manager, class_ptr);
Assert(entity_base != NULL);
serialize_entity(ar, entity_base);
if (entity_base->ID >= g_entity_manager_id)
{
g_entity_manager_id = entity_base->ID + 1;
}
entity_base->is_dirty = false;
return entity_base;
}
void update_local_counter(EntityManager* manager, EntityID entity_id)
{
Assert(manager != NULL);
uint8 user_id = get_local_user_id();
if (get_user_id(entity_id) == user_id)
{
uint64 counter = get_local_counter(entity_id);
if (counter >= manager->next_local_counter)
{
manager->next_local_counter = counter + 1ULL;
}
}
}
-25
View File
@@ -1,25 +0,0 @@
#include <Entity/EntityManager.h>
#include <Entity/Entity.h>
namespace Game
{
namespace
{
EntityManager Manager;
}
EntityID EntityManager::ID = 0;
void InitEntityManager() {}
EntityManager& GetEntityManager()
{
return Manager;
}
void RegisterEntity(EntityManager& /*manager*/, Entity* entity)
{
entity->ID = EntityManager::ID++;
}
} // namespace Game
+34 -13
View File
@@ -1,19 +1,40 @@
#pragma once
#include <Core/Common/CoreTypes.h>
#include <Core/Container/Vector.h>
#include <Entity/entity_common.h>
namespace Game
typedef struct World World;
typedef struct EntityTemplate EntityTemplate;
typedef struct Entity Entity;
typedef struct Class Class;
typedef struct Archive Archive;
typedef struct typed_entity_array typed_entity_array;
struct typed_entity_array
{
using EntityID = uint64_t;
Arena* arena;
EntityTemplate* array;
size_t count;
};
struct Entity;
struct EntityManager
{
static EntityID ID;
// May be this should contains the allocator for each entity types
};
typedef struct EntityManager EntityManager;
struct EntityManager
{
uint64 next_local_counter;
void InitEntityManager();
EntityManager& GetEntityManager();
void RegisterEntity(EntityManager& manager, Entity* entity);
} // namespace Game
// TODO: Should be a pool
VectorArena Entities;
typed_entity_array by_type[Entity_Type_Count];
};
extern EntityID g_entity_manager_id;
void InitEntityManager(World* world);
void ShutdownEntityManager(void);
EntityManager* GetEntityManager(void);
EntityTemplate* RegisterEntity(EntityManager* manager, Entity* base, DerivedType entity);
Entity* allocate_entity(EntityManager* manager, const Class* derived_type_class);
void UpdateEntityManager(EntityManager* manager);
Entity* deserialize_entity(Archive* archive, EntityManager* manager);
void update_local_counter(EntityManager* manager, EntityID entity_id);
+20
View File
@@ -0,0 +1,20 @@
#include <Entity/entity_common.h>
#include <Entity/Entity.h>
#include <Entity/entity_types.h>
// clang-format off
#define AS_STR(name) #name
const char* kEntity_type_names[] = {
ENTITY_TYPE_LIST(AS_STR)
"Count"
};
#undef AS_STR
#define AS_CLASS(name) &class_kind_##name
const Class* kEntity_type_class_ptr[] = {
ENTITY_TYPE_LIST(AS_CLASS)
NULL
};
#undef AS_CLASS
// clang-format on
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <Engine/Class.h>
typedef void* DerivedType;
typedef uint64 EntityID;
#define kInvalid_user_id ((uint8)0U)
#define kInvalid_entity_id ((EntityID)0ULL)
#define kEntityID_user_id_shift ((EntityID)56ULL)
#define kEntityID_user_id_mask ((EntityID)0xFF00000000000000ULL)
// clang-format off
#define ENTITY_TYPE_LIST(X) X(Inert),
typedef uint8 Entity_Type;
#define AS_ENUM(name) Entity_Type_##name
enum
{
ENTITY_TYPE_LIST(AS_ENUM)
Entity_Type_Count
};
#undef AS_ENUM
extern const char* kEntity_type_names[];
extern const Class* kEntity_type_class_ptr[];
#define ENTITY(kind) ((uint8)Entity_Type_##kind)
// clang-format on
typedef struct Entity Entity;
#define DECLARE_ENTITY() Entity* base;
#define DEFINE_ENTITY_VERSIONED(entity, version_num) \
const Class class_kind_##entity = { \
.Name = ConstString(#entity), \
.kind = (uint8)Entity_Type_##entity, \
.version = (version_num), \
.base_class = &class_kind_Entity, \
.initialize_fct = init_##entity, \
.serialize_fct = serialize_##entity, \
.size_of = sizeof(entity), \
.alignment = AlignOf(entity) \
};
// Note: Needed by every entity class (see define above)
extern const Class class_kind_Entity;
+22
View File
@@ -0,0 +1,22 @@
#include <Entity/entity_types.h>
#include <Core/Common/serialization.h>
internal void init_Inert(void* payload)
{
Assert(payload != NULL);
Inert* inert = (Inert*)payload;
inert->base = NULL;
inert->MeshInstance = indexMax;
}
internal void serialize_Inert(Archive* ar, uint16 version, void* payload)
{
(void)version;
Assert(ar != NULL);
Assert(payload != NULL);
Inert* inert = (Inert*)payload;
SERIALIZE_U64(ar, MeshInstance, &inert->MeshInstance);
}
DEFINE_ENTITY_VERSIONED(Inert, 1)
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <Entity/entity_common.h>
typedef struct Inert Inert;
struct Inert
{
DECLARE_ENTITY()
index_t MeshInstance;
};
DECLARE_CLASS(Inert)
-89
View File
@@ -1,89 +0,0 @@
// Game - Lib/Dll
//------------------------------------------------------------------------------
{
.ProjectName = 'Game'
.ProjectPath = 'Game'
.GAME_DLL_NAME = '$ProjectName$'
#if exists( GAME_BUILD_ID )
{
#import GAME_BUILD_ID
^GAME_DLL_NAME + '-$GAME_BUILD_ID$'
}
#endif
// Library
//--------------------------------------------------------------------------
ForEach( .BuildConfig in .BuildConfigs )
{
Using( .BuildConfig )
.OutputBase + '\$Platform$-$BuildConfigName$'
// Unity
//--------------------------------------------------------------------------
Unity( '$ProjectName$-Unity-$Platform$-$BuildConfigName$' )
{
.UnityInputPath = '$ProjectPath$/'
.UnityOutputPath = '$OutputBase$/$ProjectPath$/'
.UnityOutputPattern = '$ProjectName$_Unity*.cpp'
}
// Library
//--------------------------------------------------------------------------
ObjectList( '$ProjectName$-Objs-$Platform$-$BuildConfigName$' )
{
// Input (Unity)
.CompilerInputUnity = '$ProjectName$-Unity-$Platform$-$BuildConfigName$'
// Extra Compiler Options
.CompilerOptions + ' "-IJuliet/include"'
+ ' "-IGame"'
.CompilerOptions + ' -DJULIET_EXPORT'
#if __WINDOWS__
.CompilerOptions + ' -DJULIET_WIN32'
#endif
.CompilerOutputPath = '$OutputBase$/$ProjectPath$/'
}
DLL( '$ProjectName$-Lib-$Platform$-$BuildConfigName$' )
{
.Libraries = { '$ProjectName$-Objs-$Platform$-$BuildConfigName$',
'Juliet-Lib-$Platform$-$BuildConfigName$' } // Link Juliet (Lib or Import Lib)
#if exists( GAME_DLL_NAME )
.LinkerOutput = '$BinPath$/$Platform$-$BuildConfigName$/$GAME_DLL_NAME$.dll'
#else
.LinkerOutput = '$BinPath$/$Platform$-$BuildConfigName$/$ProjectName$.dll'
#endif
#if __WINDOWS__
.LinkerOptions + ' /DLL'
.LinkerOptions + .CommonWinLibs
.CRTLibs = .CRTLibs_Dynamic
If ( .BuildConfigName == 'Debug' )
{
^CRTLibs = .CRTLibs_DynamicDebug
}
.LinkerOptions + .CRTLibs
#endif
}
Alias( '$ProjectName$-$Platform$-$BuildConfigName$' ) { .Targets = '$ProjectName$-Lib-$Platform$-$BuildConfigName$' }
^'Targets_$Platform$_$BuildConfigName$' + { '$ProjectName$-$Platform$-$BuildConfigName$' }
#if __WINDOWS__
.ProjectConfig = [ Using( .'Project_$Platform$_$BuildConfigName$' ) .Target = '$ProjectName$-$Platform$-$BuildConfigName$' ]
^ProjectConfigs + .ProjectConfig
#endif
}
VCXProject( '$ProjectName$' )
{
.ProjectOutput = '$ProjectPath$/$ProjectName$.vcxproj'
.ProjectBasePath = '$ProjectPath$/'
.ProjectInputPaths = .ProjectBasePath
.ProjectConfigs = .ProjectConfigs
}
}
-364
View File
@@ -1,364 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="x64-Debug|x64">
<Configuration>x64-Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64-Profile|x64">
<Configuration>x64-Profile</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64-Release|x64">
<Configuration>x64-Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64Clang-Debug|x64">
<Configuration>x64Clang-Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64Clang-Profile|x64">
<Configuration>x64Clang-Profile</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64Clang-Release|x64">
<Configuration>x64Clang-Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64-Debug|x64">
<Configuration>x64-Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64-Profile|x64">
<Configuration>x64-Profile</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64-Release|x64">
<Configuration>x64-Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64Clang-Debug|x64">
<Configuration>x64Clang-Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64Clang-Profile|x64">
<Configuration>x64Clang-Profile</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64Clang-Release|x64">
<Configuration>x64Clang-Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="Entity\Entity.h" />
<CustomBuild Include="Entity\EntityManager.cpp" />
<CustomBuild Include="Entity\EntityManager.h" />
<CustomBuild Include="Game.bff" />
<CustomBuild Include="game.cpp" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{b1d040d0-6c94-4f93-bc2a-7f5284b7d434}</ProjectGuid>
<Keyword>MakeFileProj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;DEBUG;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;DEBUG;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Juliet\src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;DEBUG;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Game;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Game;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Game;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;DEBUG;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Game;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Game;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;..\Juliet\include;..\Game;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<Import Condition="'$(ConfigurationType)' == 'Makefile' and Exists('$(VCTargetsPath)\Platforms\$(Platform)\SCE.Makefile.$(Platform).targets')" Project="$(VCTargetsPath)\Platforms\$(Platform)\SCE.Makefile.$(Platform).targets" />
<Import Condition="'$(ConfigurationType)' == 'Makefile' and '$(AndroidAPILevel)' != '' and Exists('$(VCTargetsPath)\Application Type\$(ApplicationType)\$(ApplicationTypeRevision)\Android.Common.targets')" Project="$(VCTargetsPath)\Application Type\$(ApplicationType)\$(ApplicationTypeRevision)\Android.Common.targets" />
</Project>
-23
View File
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<CustomBuild Include="Entity\Entity.h">
<Filter>Entity</Filter>
</CustomBuild>
<CustomBuild Include="Entity\EntityManager.cpp">
<Filter>Entity</Filter>
</CustomBuild>
<CustomBuild Include="Entity\EntityManager.h">
<Filter>Entity</Filter>
</CustomBuild>
<CustomBuild Include="Game.bff">
</CustomBuild>
<CustomBuild Include="game.cpp">
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Filter Include="Entity">
<UniqueIdentifier>{6051ee0a-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
@@ -0,0 +1,439 @@
# Juliet Engine: Serialization & Text Archive Architecture
## Technical Specification & Architectural Design
---
## 1. Executive Summary & Architecture Goals
### 1.1 Context & Motivation
Juliet historically relied on monolithic, packed binary blobs for world and entity persistence. While fast to read as raw byte offsets, binary serialization suffers from three major flaws:
1. **Merge Incompatibility**: Binary assets cannot be merged or diffed in version control systems (Git / Perforce), causing unresolvable binary conflicts and data loss.
2. **Schema Rigidity**: Adding, removing, or reordering a single struct field invalidates all existing binary files unless complex manual byte-offset mapping tables are maintained.
3. **Opacity**: Designers and engineers cannot inspect, debug, or patch asset properties in a standard text editor.
### 1.2 Architectural Goals
The `.jasset` text archive framework replaces legacy binary blobs with a human-readable, diff-friendly property serialization pipeline adhering to Juliet's systems programming principles:
- **Zero Dynamic Heap Allocations**: Parsing, tokenization, formatting, and buffer transformations execute entirely within Juliet memory arenas (`Arena`, `TempArena`, `scratch_begin` / `scratch_end`). Standard library containers (`std::string`, `std::vector`) and raw heap allocators (`malloc`, `new`) are forbidden.
- **Zero-Copy In-Memory Tokenization**: Files are loaded into arena memory once via `LoadFile`. The tokenizer parses properties into lightweight slices represented by Juliet's `String` (`char* Str; size_t Size;`), referencing existing file buffer bytes without string duplication.
- **$O(1)$ Property Lookup via Compile-Time & Runtime CRC32**: Property keys in text files are hashed once during tokenization into 32-bit CRC values. In code, lookups utilize compile-time hashed literals (`operator""_crc32`). Property resolution is a single 32-bit integer comparison.
- **Symmetric Single-Function Serialization**: A single `serialize` implementation per entity or data structure handles both Save and Load paths, guaranteeing read and write schemas never diverge.
- **Graceful Forward/Backward Compatibility**: Missing keys during load automatically preserve struct default values. Unknown keys present in newer asset files are safely ignored without parse failure.
- **Two-Tier Decoupled Versioning via Generalized `Class`**: Core engine entity properties (`; version`) and derived gameplay class properties (`; class_version`) are versioned independently through their respective `Class` descriptors.
- **In-Place Schema Migration**: Deprecated fields no longer present in C++ structs are read into temporary stack variables during load using standard `SERIALIZE` calls guarded by `if (ar.loading && version < N)`, seamlessly converting legacy data without struct pollution or persisting obsolete keys on subsequent saves.
- **Clean Warning-Free C++**: Fully conforming to Juliet coding guidelines: strict assertions (`Assert`), `[[nodiscard]]`, `auto*`/`auto&`, mandatory braces, and `static_cast`/`reinterpret_cast`.
---
## 2. The `.jasset` Text Format Specification
### 2.1 Grammar & Structural Rules
The `.jasset` format uses a line-oriented, key-value property hierarchy designed for visual clarity and clean Git diffs.
```ebnf
AssetFile ::= { CommentLine | EmptyLine | PropertyDeclaration } ;
CommentLine ::= ( "#" | "//" ) { Character } LineEnding ;
EmptyLine ::= { Whitespace } LineEnding ;
PropertyDeclaration ::= KeyHeader LineEnding ValueBlock ;
KeyHeader ::= ";" { Whitespace } Identifier ;
ValueBlock ::= { ValueLine LineEnding } ;
ValueLine ::= { Whitespace } ValueString { Whitespace } ;
LineEnding ::= "\r\n" | "\n" ;
Identifier ::= [a-zA-Z_][a-zA-Z0-9_]* ;
```
#### Formatting Rules:
1. **Key Declarations**: A property begins with a semicolon `;` followed by optional whitespace and a case-sensitive identifier (e.g. `; position`).
2. **Value Blocks**: The line(s) immediately following a key header contain its value payload.
3. **Comments**: Any line whose first non-whitespace character is `#` or `//` is treated as a comment and ignored. Inline comments on property lines are forbidden.
4. **Whitespace**: Leading and trailing spaces or tabs on both keys and values are stripped during tokenization.
5. **Line Endings**: Both Windows CRLF (`\r\n`) and Linux LF (`\n`) are transparently accepted.
### 2.2 Formatting Specifications
#### Scalar Types
Scalars are formatted as decimal representations on a single line:
```
; max_speed
180.500000
; gear_count
6
; turbo
true
```
- Floats: Output via `%.9g` or `%f`.
- Integers: Signed (`%d`, `%lld`) and unsigned (`%u`, `%llu`).
- Booleans: Case-insensitive `true` / `false` or `1` / `0`.
#### Vector Types (`Vector4`)
Multi-component vectors are space-delimited on a single value line:
```
; position
10.0 20.0 30.0 1.0
```
#### String Types (`String`)
Strings containing spaces are wrapped in double quotes `"..."`. Quotes are automatically stripped upon loading and emitted during saving when spaces are present:
```
; name
"Paladin Hero"
```
#### Asset File Example (`Entity_01.jasset`)
```
# Juliet Entity Asset File
; version
1
; id
1001
; position
12.500000 0.000000 45.200000 1.000000
; class
Inert
; class_version
1
; mesh_instance
42
```
---
## 3. Zero-Copy Tokenization & Fast Property Lookup
### 3.1 Data Structures (`Core/Common/serialization.h`)
To eliminate heap fragmentation, the parser loads the entire `.jasset` file into contiguous arena memory and parses it into a flat array of lightweight slices.
```cpp
struct ArchivePropertyNode
{
String key;
String value;
uint32 key_crc;
bool consumed;
};
struct ParsedArchive
{
ArchivePropertyNode* nodes = nullptr;
uint32 property_count = 0;
};
```
- `key`: Sliced `String` referencing the key name.
- `value`: Sliced `String` directly referencing file buffer bytes (zero-copy).
- `key_crc`: 32-bit CRC hash computed once during tokenization.
- `consumed`: Initialized to `false`. Set to `true` whenever queried by `find_property`.
### 3.2 Dual-Mode CRC32 (`Core/Common/CRC32.h`)
Lookups rely on compile-time string hashing via `constexpr` / `consteval`:
```cpp
[[nodiscard]] constexpr uint32 crc32(const char* str, size_t length);
[[nodiscard]] constexpr uint32 crc32(String str);
[[nodiscard]] consteval uint32 operator""_crc32(const char* str, size_t length);
```
### 3.3 Tokenization API (`tokenize_archive`)
```cpp
JULIET_API ParsedArchive tokenize_archive(NonNullPtr<Arena> arena, ByteBuffer file_buffer);
```
**Algorithm Invariants**:
1. **Pass 1 (Count)**: Scans the buffer to count `;` key headers at line starts, allocating the exact node array in `arena`.
2. **Pass 2 (Extract)**: Slices key and value `String`s, trims whitespace, computes `key_crc = crc32(key)`, and populates nodes. Skips `#` and `//` comments.
### 3.4 Property Lookup & Audit API
```cpp
JULIET_API ArchivePropertyNode* find_property(NonNullPtr<ParsedArchive> archive, uint32 property_crc);
#if JULIET_DEBUG
JULIET_API void audit_unconsumed_properties(NonNullPtr<ParsedArchive> archive, String context_name);
#endif
```
- `find_property`: Performs an $O(1)$ integer comparison against `key_crc`. When found, marks `node->consumed = true`.
- `audit_unconsumed_properties`: Iterates through all nodes in debug builds and logs warnings for any property with `consumed == false`, catching typos or abandoned schema fields.
---
## 4. The `Archive` Context Struct & Streaming I/O
### 4.1 Struct Definition (`Core/Common/serialization.h`)
The `Archive` struct unifies loading and saving state into a single decoupled context:
```cpp
struct Archive
{
Arena* arena;
bool loading;
ParsedArchive base = {};
IOStream* stream = nullptr;
// Legacy binary support fields (to be deprecated)
void* base_ptr = nullptr;
index_t offset = 0;
};
```
- When `loading == true`: Reads properties from `base.nodes` via `find_property`.
- When `loading == false`: Writes formatted key-value pairs directly to `stream`.
### 4.2 Property Header Formatting
```cpp
JULIET_API void write_property_header(Archive& archive, String property_name);
```
Emits `; <property_name>\n` directly to `ar.stream` with zero intermediate heap buffers.
---
## 5. Property Serialization API & Helpers
### 5.1 Unified Serialization Idiom
All property serialization uses a single template function:
```cpp
template <typename Type>
bool serialize(Archive& ar, String property_name, uint32 property_crc, Type& value)
{
Assert(IsValid(property_name));
bool result = false;
if (ar.loading)
{
if (auto* prop = find_property(&ar.base, property_crc))
{
if (read_prop(ar, prop->value, value))
{
result = true;
}
}
}
else
{
write_property_header(ar, property_name);
write(ar.stream, value);
result = true;
}
return result;
}
```
### 5.2 Convenience Macros
```cpp
#define SERIALIZE(ar, name, var) serialize((ar), ConstString(#name), #name##_crc32, (var))
#define SERIALIZE_SIMPLE(ar, var) SERIALIZE(ar, var, var)
```
- `SERIALIZE(ar, id, entity->ID)`: Serializes property named `"id"` with `"id"_crc32`.
- `SERIALIZE_SIMPLE(ar, position)`: Uses variable identifier as property name.
### 5.3 Supported Type Conversions
Conversion between text and memory is handled by overloaded `read` and `write` primitives:
| C++ Type | Text Format | Conversion Primitive |
| :--- | :--- | :--- |
| `float` | `180.500000` | `strtof` / `IOPrintf("%.9g")` |
| `int8`, `int16`, `int32`, `int64` | `42` / `-100` | `strtol`, `strtoll` / `IOPrintf("%d")` |
| `uint8`, `uint16`, `uint32`, `uint64` | `1001` / `0x...` | `strtoul`, `strtoull` / `IOPrintf("%u")` |
| `bool` | `true` / `false` | `true/false/1/0` string compare / `IOPrintf` |
| `Vector4` | `10.0 20.0 30.0 1.0` | Space-delimited float parse / `IOPrintf` |
| `String` | `"Paladin Hero"` | Arena-allocated copy, quote strip / `IOPrintf` |
---
## 6. Two-Tier Versioning & Generalized `Class` Architecture
### 6.1 Architectural Principle
To prevent monolithic engine updates from forcing all gameplay assets to re-version, schema versions are decoupled into two tiers:
1. **Base Version (`; version`)**: Managed by root classes (e.g. `Entity::kind->version`). Governs core engine properties (`id`, `position`).
2. **Derived Version (`; class_version`)**: Managed by derived classes (e.g. `Inert::kind->version`). Governs gameplay-specific component properties.
### 6.2 The `Class` Descriptor (`Engine/Class.h`)
Every serializable entity or component is described by an immutable `Class` instance:
```cpp
using serialize_fct_type = void (*)(Archive& ar, uint16 version, void* payload);
using default_init_fct_type = void (*)(void* payload);
struct Class
{
uint32 CRC;
uint8 kind;
uint16 version;
const Class* base_class;
serialize_fct_type serialize_fct;
default_init_fct_type default_init_fct;
size_t size_of;
size_t alignment;
#if JULIET_DEBUG
String Name;
#endif
};
```
### 6.3 Class Registration Macros
```cpp
#define DECLARE_CLASS() \
static Class* kind;
#define DEFINE_CLASS_VERSIONED(cls, version, base_class, serialize_fct) \
inline void default_init_##cls(void* payload) { *static_cast<cls*>(payload) = cls{}; } \
constexpr Class classKind##cls = \
MakeClass(ConstString(#cls), 0, (version), (base_class), sizeof(cls), alignof(cls), \
(serialize_fct), default_init_##cls); \
Class* cls::kind = const_cast<Class*>(&classKind##cls);
```
For derived entity types, `DECLARE_ENTITY()` and `DEFINE_ENTITY_VERSIONED` compose cleanly:
```cpp
#define DECLARE_ENTITY() \
Entity* base; \
DECLARE_CLASS()
#define DEFINE_ENTITY_VERSIONED(entity, version, serialize_fct) \
inline void default_init_##entity(void* payload) { *static_cast<entity*>(payload) = entity{}; } \
constexpr Class entityKind##entity = MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, (version), \
&classKindEntity, sizeof(entity), alignof(entity), \
(serialize_fct), default_init_##entity); \
Class* entity::kind = const_cast<Class*>(&entityKind##entity);
```
### 6.4 Universal Class Serializer (`Engine/class.cpp`)
```cpp
void serialize(Archive& ar, NonNullPtr<Class> cls, void* instance)
{
Assert(instance != nullptr);
uint16 version = cls->version;
if (cls->base_class)
{
serialize(ar, ConstString("class_version"), "class_version"_crc32, version);
}
else
{
serialize(ar, ConstString("version"), "version"_crc32, version);
}
if (cls->serialize_fct)
{
cls->serialize_fct(ar, version, instance);
}
}
```
### 6.5 Runtime Type Queries (`IsA`)
Polymorphic type safety is resolved without virtual tables or RTTI:
```cpp
bool IsA(const Class& query, const Class* target);
template <typename TargetType>
bool IsA(const Class& cls)
{
return IsA(cls, TargetType::kind);
}
```
### 6.6 Entity Serialization Composition
An entity instance composes base `Entity` properties and derived component properties:
```cpp
void serialize_entity(Archive& ar, uint16 /*version*/, void* payload)
{
Assert(payload != nullptr);
auto* entity = static_cast<Entity*>(payload);
SERIALIZE(ar, id, entity->ID);
SERIALIZE(ar, position, entity->position);
}
DEFINE_CLASS_VERSIONED(Entity, 1, nullptr, serialize_entity)
void serialize(Archive& ar, NonNullPtr<Entity> entity)
{
// 1. Serialize base Entity properties (reads/writes '; version')
serialize(ar, Entity::kind, entity.Get());
// 2. Serialize derived component properties (reads/writes '; class_version')
if (entity->derived_kind != nullptr && entity->derived != nullptr)
{
serialize(ar, entity->derived_kind, entity->derived);
}
}
```
---
## 7. In-Place Schema Migration & Deprecation
### 7.1 Deprecation Principle
When gameplay code evolves, obsolete member variables are deleted from active C++ structs to avoid memory waste and code clutter. Obsolete properties are migrated exclusively during loading using temporary local stack variables.
### 7.2 The Stack-Allocated Migration Idiom
In the class's `serialize_fct(Archive& ar, uint16 version, void* payload)`:
1. When `ar.loading == true` and `version < N`:
- Declare a temporary variable on the stack matching the legacy type.
- Call `SERIALIZE(ar, old_field_name, deprecated_var)`.
- If present, transform the legacy data into the modern struct field(s).
2. When saving (`ar.loading == false`):
- The migration block is skipped. Only modern struct properties are written.
- On the next save, obsolete keys are automatically purged from disk.
```cpp
void serialize_projectile(Archive& ar, uint16 version, void* payload)
{
Assert(payload != nullptr);
auto* projectile = static_cast<Projectile*>(payload);
SERIALIZE(ar, damage, projectile->damage);
if (ar.loading && version < 2)
{
// Migrating v1 scalar 'speed' into modern Vector4 'velocity'
float deprecated_speed = 0.0f;
if (SERIALIZE(ar, speed, deprecated_speed))
{
projectile->velocity = Vector4{ deprecated_speed, 0.0f, 0.0f, 0.0f };
}
}
else
{
SERIALIZE(ar, velocity, projectile->velocity);
}
}
```
---
## 8. Verification & Unit Testing Framework
### 8.1 Engine-Level Test Runner (`Juliet/src/UnitTest/`)
Unit testing lives inside the Juliet engine layer (`Juliet/src/UnitTest/serialization_test.cpp`) and executes during engine startup in debug builds via `UnitTest::RunUnitTests()` in `RunUnitTests.cpp`.
### 8.2 Test Coverage Matrix
| Test Function | Target Feature | Validation Criteria |
| :--- | :--- | :--- |
| `test_parser_tokenization` | Zero-copy text parser | Validates handling of `#` and `//` comments, whitespace trimming, mixed CRLF/LF, and fast CRC property lookups. |
| `test_default_value_retention` | Partial schema loading | Validates that missing properties in partial files preserve existing struct default values without corruption. |
| `test_deprecation_migration` | Stack-based schema migration | Loads a v1 asset containing obsolete `; speed`, verifies `version = 1` is received, and validates migration into modern fields. |
| `test_class_inheritance` | Runtime `IsA` queries | Validates polymorphic inheritance checks across base and derived `Class` instances. |
| `test_string_and_vector4` | Primitives & text quoting | Verifies parsing and quote handling of `String` and multi-component `Vector4`. |
---
## 9. Deliverables & File Summary
| File | Responsibilities |
| :--- | :--- |
| [`Juliet/include/Core/Common/serialization.h`](file:///w:/Classified/Juliet/Juliet/include/Core/Common/serialization.h) | `ArchivePropertyNode`, `ParsedArchive`, `Archive` struct, `serialize` template, and `SERIALIZE` macros. |
| [`Juliet/src/Core/Common/serialization.cpp`](file:///w:/Classified/Juliet/Juliet/src/Core/Common/serialization.cpp) | `tokenize_archive`, `find_property`, `audit_unconsumed_properties`, `read_prop`, `read`, and `write` primitives. |
| [`Juliet/include/Engine/Class.h`](file:///w:/Classified/Juliet/Juliet/include/Engine/Class.h) | `Class` struct, `MakeClass`, `DECLARE_CLASS()`, `DEFINE_CLASS_VERSIONED`, and `IsA` declarations. |
| [`Juliet/src/Engine/class.cpp`](file:///w:/Classified/Juliet/Juliet/src/Engine/class.cpp) | Universal `serialize(Archive&, NonNullPtr<Class>, void*)` and runtime `IsA` traversal. |
| [`Juliet/src/UnitTest/serialization_test.cpp`](file:///w:/Classified/Juliet/Juliet/src/UnitTest/serialization_test.cpp) | Exhaustive unit tests for tokenization, defaults retention, version migration, and type queries. |
| [`Game/Entity/Entity.h`](file:///w:/Classified/Juliet/Game/Entity/Entity.h) | `DECLARE_ENTITY()`, `DEFINE_ENTITY_VERSIONED`, and `Entity` struct definition. |
| [`Game/Entity/Entity.cpp`](file:///w:/Classified/Juliet/Game/Entity/Entity.cpp) | `serialize_entity` registration and two-tier `serialize(Archive&, NonNullPtr<Entity>)` composition. |
@@ -0,0 +1,818 @@
# Juliet Game Engine Architecture Specification
## Document: 02 - Entity Allocation & In-Place Lifecycle
**Document ID:** JULIET-SPEC-002
**Status:** Approved for Implementation
**Author:** Senior Engine Architect
**Subsystems:** `Game/Entity`, `Game/Data`, `Game/UnitTest`
**Target Files:**
- [`Entity.h`](file:///w:/Classified/Juliet/Game/Entity/Entity.h)
- [`Entity.cpp`](file:///w:/Classified/Juliet/Game/Entity/Entity.cpp)
- [`EntityManager.h`](file:///w:/Classified/Juliet/Game/Entity/EntityManager.h)
- [`EntityManager.cpp`](file:///w:/Classified/Juliet/Game/Entity/EntityManager.cpp)
- [`World.h`](file:///w:/Classified/Juliet/Game/Data/World.h)
- [`World.cpp`](file:///w:/Classified/Juliet/Game/Data/World.cpp)
- [`WorldUnitTest.h`](file:///w:/Classified/Juliet/Game/UnitTest/WorldUnitTest.h)
- [`WorldUnitTest.cpp`](file:///w:/Classified/Juliet/Game/UnitTest/WorldUnitTest.cpp)
---
## 1. Executive Summary & Problem Statement
### 1.1 Background & Context
The Juliet game engine organizes game entities using a hybrid data-oriented architecture:
1. A flat array of **Base Entities** (`Entity`) encapsulating universal properties: unique 64-bit ID, runtime type reflection pointer (`Class* Kind`), spatial coordinates (`X, Y, Z`), and an opaque pointer to the derived payload (`DerivedType Derived`).
2. Type-segregated contiguous arrays of **Derived Entities** (`Inert`, etc.) stored in dedicated per-type memory arenas (`typed_entity_array`).
This design is intended to provide maximum cache efficiency during spatial and general-purpose entity processing, while retaining dense SIMD-friendly streaming for type-specific systems (e.g., transform updates on `Inert` mesh instances).
### 1.2 Root-Cause Analysis of `Game/Data/World.cpp` Loading Flaws
In the initial implementation of entity serialization in [`Game/Data/World.cpp`](file:///w:/Classified/Juliet/Game/Data/World.cpp#L40-L68), loading was fundamentally broken and incomplete:
```cpp
// Existing flawed deserialization in World.cpp
for (typed_entity_array& type : entityManager.by_type)
{
serialize_elem(ar, type.count);
if (type.count > 0)
{
// Unserialize the base entity to get informations
Entity entity;
serialize(ar, &entity);
RegisterBaseEntity(entityManager, entity);
}
}
```
This implementation suffers from several fatal defects:
1. **Single-Element Iteration Bug:** It uses `if (type.count > 0)` instead of a loop `for (size_t i = 0; i < type.count; ++i)`, deserializing at most one single entity per type bucket, leaving all subsequent entities in the stream unread and corrupting the archive read offset.
2. **Missing Derived Allocation:** It invokes `RegisterBaseEntity(entityManager, entity)`, which merely pushes the stack-allocated `Entity` into `manager.Entities`. The derived payload arena (`type.arena`) is completely untouched: `type.array` remains null, `type.count` in the manager is desynchronized, and `entity.Derived` remains unassigned or points to an invalid address.
3. **Invalid Pointer in `RegisterEntity`:** In [`Game/Entity/EntityManager.cpp`](file:///w:/Classified/Juliet/Game/Entity/EntityManager.cpp#L46-L66), `RegisterEntity` assigns `base->Derived = entity` before pushing `*base` into `manager.Entities`. The parameter `entity` is a pointer to caller-provided memory (often stack-allocated in helper functions like `MakeEntity`). When `ArenaPushSize` later allocates the persistent derived memory block, `base->Derived` stored inside `manager.Entities.Back()` is **never updated**—it remains dangling, pointing to the transient caller stack!
### 1.3 The "Chicken-and-Egg" Stack Allocation Dilemma
The existing registration API requires a pre-existing derived instance:
```cpp
entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity);
```
During runtime programmatic creation via `MakeEntity<T>()`, a temporary instance of `T` is created on the stack and passed by pointer:
```cpp
template <typename EntityType>
EntityType* MakeEntity(EntityManager& manager, float x, float y, float z)
{
EntityType result; // Stack allocation
Entity base; // Stack allocation
base.X = x;
base.Y = y;
base.Z = z;
base.Kind = EntityType::Kind;
return (EntityType*)RegisterEntity(manager, &base, &result);
}
```
When loading an entity from a stream or disk file, **the type is not known at compile time**. The engine reads a runtime type tag (`uint8 kind` or `uint32 CRC`), looks up the reflection metadata (`Class*`), and must instantiate the entity dynamically.
Because C++ does not permit allocating a dynamic struct of unknown type on the stack, and because Juliet strictly forbids heap allocations (`malloc`, `new`, `std::vector`), deserialization cannot construct a temporary instance on the stack to pass to `RegisterEntity`.
This is the classic **chicken-and-egg memory problem**:
- `RegisterEntity` requires an existing instance in memory to copy from.
- Deserialization requires an allocated memory buffer to deserialize into.
### 1.4 Architectural Objectives
This specification establishes a robust in-place lifecycle pipeline that completely eliminates stack temporaries and dynamic heap allocations:
1. **Direct In-Place Allocation:** Introduce `AllocateEntity(EntityManager& manager, Class* classPtr)` which allocates both the base `Entity` and the derived struct directly within their respective engine memory arenas.
2. **Bidirectional Pointer Integrity:** Wire mutual pointers (`base->derived` and `derived->base`) at allocation time before any field deserialization begins.
3. **In-Place Stream Deserialization:** Read class reflection metadata first, invoke `AllocateEntity`, and stream base and derived properties directly into arena-resident memory.
4. **Isolated Entity Assets (`.jasset`):** Transition from a monolithic `world.bin` to a modular one-file-per-entity architecture (`Assets/Entities/{ID}.jasset`).
5. **Dirty Tracking & Optimal Saves:** Introduce an `is_dirty` flag on `Entity` to avoid rewriting unchanged entity files, minimizing disk I/O and eliminating spurious Git repository modifications.
6. **Robust Deletion Lifecycle:** Decouple in-memory removal (`RemoveAtFast` with pointer fixup) from disk synchronization using `World::PendingDeletions`.
---
## 2. The Dual Arena Memory Model in `EntityManager`
### 2.1 The Need for Dual Storage
Game engines execute systems with vastly different cache locality profiles:
- **Spatial / Frustum Culling / Transform Sync:** Iterates every entity in the world, needing only `X, Y, Z`, bounding spheres, and base status flags.
- **Specialized Logic / Render Updates:** Iterates only entities possessing specific components (e.g., `Inert` static meshes requiring instance transform updates to the GPU bindless descriptor table).
Storing large monolithic polymorphic structs in a single array causes severe cache line pollution during spatial passes. Conversely, storing entities in fragmented individual allocations introduces cache misses and pointer-chasing overhead.
Juliet resolves this with a **Dual Arena Memory Model**:
```
+---------------------------------------------------------------------------------------------+
| EntityManager |
+---------------------------------------------------------------------------------------------+
| |
| manager.Entities (VectorArena<Entity, 100'000>) |
| +------------------------------------+------------------------------------+ |
| | Entity 0 (ID=1001, X, Y, Z) | Entity 1 (ID=1002, X, Y, Z) | ... |
| | Derived ------------------------+ | Derived ---------------------+ | |
| +---------------------------------|--+------------------------------|-----+ |
| | | |
| v v |
| manager.by_type[ENTITY(Inert)].arena |
| +------------------------------------+------------------------------------+ |
| | Inert 0 (MeshInstance=4) | Inert 1 (MeshInstance=12) | ... |
| | Base ---------------------------+ | Base ------------------------+ | |
| +---------------------------------|--+------------------------------|-----+ |
| +---------------------------------+ |
+---------------------------------------------------------------------------------------------+
```
### 2.2 `manager.Entities`: Cache-Friendly Base Entity Vector
Base entities reside in a pre-reserved contiguous array:
```cpp
VectorArena<Entity, 100'000> Entities;
```
- **Capacity:** Fixed reserve of 100,000 entities allocated from the `WorldArena`.
- **Memory Footprint:**
$$\text{sizeof(Entity)} = 8\text{ (ID)} + 8\text{ (derived\_kind)} + 8\text{ (derived)} + 16\text{ (position)} + 1\text{ (is\_dirty)} + 7\text{ (Padding)} = 48\text{ bytes}$$
Total reserved space: $100{,}000 \times 48\text{ bytes} \approx 4.8\text{ MB}$.
- **Access Speed:** O(1) random access by index; sequential streaming utilizes L1/L2 hardware prefetchers with zero cache line waste.
### 2.3 `manager.by_type[kind].arena`: Typed Component Arenas
Derived structs reside in per-type contiguous memory arenas:
```cpp
struct typed_entity_array
{
Arena* arena;
entity_template* array;
size_t count;
};
```
- Each `Entity_Type` index owns an isolated `Arena*` allocated during `InitEntityManager`.
- Allocations are packed linearly with alignment specified by `classPtr->alignment`.
- `array` points directly to the first element in the arena, permitting typed array indexing:
```cpp
Inert* inertArray = reinterpret_cast<Inert*>(manager.by_type[ENTITY(Inert)].array);
```
### 2.4 Mutual Back-Pointer Architecture & Invariants
Every entity instance consists of two mutually linked allocations:
1. `basePtr->Derived`: Points from `Entity` in `manager.Entities` to the derived struct in `by_type[kind].arena`.
2. `derivedPtr->Base`: Points from the derived struct (via `DECLARE_ENTITY()`) back to `Entity` in `manager.Entities`.
#### Invariant Rules:
1. **Non-Null Invariant:** For any active entity, `basePtr->Derived != nullptr` and `reinterpret_cast<entity_template*>(basePtr->Derived)->Base == basePtr`.
2. **Type Coherence Invariant:** `basePtr->Kind->kind == derivedTypeId`.
3. **Array Count Invariant:**
$$\sum_{k=0}^{\text{ENTITY(Count)}-1} \text{manager.by\_type}[k].\text{count} == \text{manager.Entities.Size()}$$
### 2.5 Pointer Stability in `VectorArena`
`VectorArena::Create` executes `Reserve(ReserveSize)` on creation:
```cpp
newManager->Entities.Create(world->WorldArena JULIET_DEBUG_PARAM("Entities"));
```
Because capacity ($100{,}000$) is fully reserved upfront in virtual address space, `VectorArena::PushBack` **never reallocates or moves existing memory**. Therefore:
- Pointers to `Entity` elements in `manager.Entities` remain absolutely stable across allocations.
- Derived struct `Base` pointers remain valid indefinitely unless an element is deleted.
- Element removal via swap-and-pop alters memory positions, requiring systematic pointer fixups (addressed in Section 5.2).
---
## 3. The Solution: `AllocateEntity(EntityManager& manager, Class* classPtr)`
### 3.1 Function Signature & Contract
The canonical allocation function is defined in `EntityManager.h`:
```cpp
[[nodiscard]] Entity* AllocateEntity(EntityManager& manager, Class* classPtr);
```
#### Preconditions:
- `classPtr != nullptr`.
- `classPtr->kind < ENTITY(Count)`.
- `classPtr->size_of >= sizeof(entity_template)`.
- `manager.Entities.Size() < manager.Entities.Capacity`.
- `manager.by_type[classPtr->kind].arena != nullptr`.
#### Postconditions:
- A new `Entity` record is appended to `manager.Entities`.
- A new typed block of `classPtr->size_of` bytes is allocated in `manager.by_type[classPtr->kind].arena`.
- The derived memory is initialized with C++ struct defaults via `classPtr->default_init_fct` (or zeroed via `MemZero` if null).
- `base->derived` points to the derived struct.
- `derived->base` points to the base `Entity`.
- `base->derived_kind` is assigned to `class_ptr`.
- `base->ID` is initialized to `0` (unassigned; populated by `MakeEntity` or deserialization).
- `base->is_dirty` is initialized to `true`.
- `typed_entity_array::count` is incremented.
- `typed_entity_array::array` is initialized if this is the first entity of this type.
### 3.2 Detailed Step-by-Step Implementation
The implementation replaces the flawed `RegisterEntity` routine in [`Game/Entity/EntityManager.cpp`](file:///w:/Classified/Juliet/Game/Entity/EntityManager.cpp):
```cpp
[[nodiscard]] Entity* AllocateEntity(EntityManager& manager, Class* derivedClassPtr)
{
Assert(derivedClassPtr != nullptr);
Assert(derivedClassPtr->kind < ENTITY(Count));
Assert(derivedClassPtr->size_of >= sizeof(entity_template));
Assert(derivedClassPtr->alignment > 0);
// 1. Allocate Base Entity in the contiguous VectorArena (ID is 0 until assigned by MakeEntity or deserialization)
Entity baseTemplate{};
baseTemplate.ID = 0;
baseTemplate.derived_kind = derivedClassPtr;
baseTemplate.derived = nullptr;
baseTemplate.position = {};
baseTemplate.is_dirty = true;
manager.Entities.PushBack(baseTemplate);
Entity* basePtr = manager.Entities.Back();
Assert(basePtr != nullptr);
// 2. Allocate derived component memory in the typed arena
typed_entity_array& typedArray = manager.by_type[derivedClassPtr->kind];
Assert(typedArray.arena != nullptr);
void* rawMemory = ArenaPushSize(
typedArray.arena,
derivedClassPtr->size_of,
derivedClassPtr->alignment,
false JULIET_DEBUG_PARAM(kEntity_type_names[derivedClassPtr->kind]));
Assert(rawMemory != nullptr);
// 3. Initialize derived component defaults via Class reflection stub
if (derivedClassPtr->default_init_fct != nullptr)
{
derivedClassPtr->default_init_fct(rawMemory);
}
else
{
MemZero(rawMemory, derivedClassPtr->size_of);
}
auto* derivedTemplate = reinterpret_cast<entity_template*>(rawMemory);
// 4. Establish mutual back-pointers
basePtr->derived = rawMemory;
derivedTemplate->base = basePtr;
// 5. Update typed array tracking
if (typedArray.array == nullptr)
{
typedArray.array = derivedTemplate;
}
typedArray.count += 1;
return basePtr;
}
```
### 3.3 Default Struct Initialization via `Class::default_init_fct`
#### The Problem with Zero-Only Initialization
If allocation only zeroes memory (`MemZero` / `0x00`), any C++ member variables with non-zero defaults (such as `index_t MeshInstance = indexMax;` or `float Density = 1.0f;`) are populated with `0`. During deserialization, if a `.jasset` file lacks that property (e.g. an older file version or an optional field), `SERIALIZE` leaves the field untouched, meaning it incorrectly remains `0` rather than its intended default sentinel value!
#### The Solution: Compile-Time Default Stub in `DEFINE_ENTITY_VERSIONED`
Every entity descriptor `Class` includes a function pointer:
```cpp
using default_init_fct_type = void (*)(void* payload);
struct Class
{
...
default_init_fct_type default_init_fct = nullptr;
};
```
When registering an entity type with `DEFINE_ENTITY_VERSIONED`, the macro automatically defines a tiny type-safe stub that aggregate value-initializes the struct:
```cpp
#define DEFINE_ENTITY_VERSIONED(entity, version, serialize_fct) \
inline void default_init_##entity(void* payload) \
{ \
*static_cast<entity*>(payload) = entity{}; \
} \
constexpr Class entityKind##entity = MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, (version), \
&classKindEntity, sizeof(entity), alignof(entity), \
(serialize_fct), default_init_##entity); \
Class* entity::kind = const_cast<Class*>(&entityKind##entity);
```
#### Architectural Advantages:
1. **Zero Boilerplate**: Developers write member initializers once in the struct definition (`index_t MeshInstance = indexMax;`).
2. **Type-Agnostic Core**: `AllocateEntity` does not need to know any C++ struct types; it unconditionally calls `derivedClassPtr->default_init_fct(rawMemory)`.
3. **Robust Deserialization**: In `deserialize_entity_in_place`, newly allocated entities already hold their canonical C++ defaults. Any properties absent in the `.jasset` file naturally retain their correct initial values.
4. **No Dynamic Heap / Placement-New**: `*static_cast<entity*>(payload) = entity{}` is pure aggregate value-assignment without `<new>` headers or exceptions.
### 3.4 Bidirectional Pointer Wiring
Notice the sequence:
1. `manager.Entities.PushBack(baseTemplate)` places the struct at its final, fixed arena address.
2. `base_ptr = manager.Entities.Back()` retrieves the persistent memory pointer.
3. `derived_class_ptr->default_init_fct(raw_memory)` initializes canonical struct defaults.
4. `derived_template->base = base_ptr` wires the derived back-pointer directly to this permanent location.
5. `base_ptr->derived = raw_memory` wires the base forward-pointer to the arena-allocated derived struct.
No stack copying occurs. Neither pointer is ever left dangling.
### 3.5 Updating Type Counts and Array Cache
`typed_entity_array` maintains:
- `typedArray.count`: The exact count of active entities of this type.
- `typedArray.array`: Pointer to the first element in the arena.
When the first entity of a given type is allocated, `typedArray.array` is set to `derived_template`. Because the arena allocates sequentially, `typedArray.array[i]` can be indexed directly with stride `class_ptr->size_of` as long as memory remains contiguous.
### 3.6 Refactoring `MakeEntity<EntityType>` Template
With `AllocateEntity` handling memory allocation, default member initialization, and mutual pointer wiring, `MakeEntity` in [`Game/Entity/Entity.h`](file:///w:/Classified/Juliet/Game/Entity/Entity.h) becomes clean, safe, and stack-free:
```cpp
template <typename EntityType>
requires EntityConcept<EntityType>
[[nodiscard]] EntityType* MakeEntity(EntityManager& manager, float x, float y, float z)
{
Entity* base_ptr = AllocateEntity(manager, EntityType::kind);
Assert(base_ptr != nullptr);
base_ptr->ID = EntityManager::ID++;
base_ptr->position.x = x;
base_ptr->position.y = y;
base_ptr->position.z = z;
base_ptr->position.w = 1.0f;
return static_cast<EntityType*>(base_ptr->derived);
}
```
---
## 4. In-Place Deserialization Pipeline
### 4.1 Asset Format Specification (`.jasset`)
To achieve robust version control and modular streaming, Juliet adopts a human-readable, Git-diffable **one-file-per-entity** disk format with extension `.jasset`.
```ini
; asset_type
entity_instance
; id
0x0100000000000042
; class
Inert
; version
1
; class_version
1
; position
0.43 0.32 1.56
; mesh_instance
12
```
#### Important: No Header Structs for Derived Types
- **Derived types NEVER require their own file header**: You do **not** write an `InertHeader`, `DoorHeader`, or `PlayerHeader`. Derived types only serialize their own member variables.
- **Universal `; version` + optional `; class_version`**: Every `.jasset` file has a universal `; version` tag. For entity assets, `; version` governs base entity properties (`kEntityBaseVersion`), while an optional `; class_version` governs derived class properties (`Class::Version`). Non-entity assets like `WorldSettings.jasset` only have `; version`.
- **No binary `EntityFileHeader` struct is needed**: Under the `; variable_name\nvalues` text format, there is no packed binary C-struct header at all. The common properties (`; id`, `; class`, `; version`, `; class_version`, `; position`) are standard text Key-Value nodes read by the exact same `Archive` parser.
### 4.2 Eliminating Intermediate Stack Allocations
Under the new pipeline:
1. The `.jasset` text file is read into memory onto a `TempArena` via `LoadFile`.
2. The property nodes are parsed into a `ParsedArchive` via `tokenize_archive(tempArena.Arena, fileBuffer, &ar.base)`.
3. The engine reads `; class` (e.g. `"Inert"`) and resolves `Class* class_ptr = find_class_by_name(class_name)`.
4. `AllocateEntity(manager, class_ptr)` is called immediately. Memory for both base and derived components is allocated in-place in their permanent engine arenas.
5. `serialize(ar, NonNullPtr<Entity>(base_ptr))` is called. Base (`Entity::kind`) and derived (`base_ptr->derived_kind`) fields stream **directly into their permanent memory arenas** without temporary staging buffers or stack copies.
### 4.3 Runtime Class Resolution
To ensure fast and safe type lookup during file deserialization, Juliet provides two tiers of type resolution:
1. **Low-Level Indexed Validation (`ResolveEntityClass`)**: An $O(1)$ array lookup into `kEntity_type_class_ptr[kind]` that validates the class pointer and verifies schema compatibility against `classPtr->CRC`. This is the core validation primitive used by binary streaming, network replication, and internal lookups.
2. **High-Level Name Resolution (`find_class_by_name`)**: Bridges the human-readable text archive format (`; class\nInert`) to the entity class registry. It computes the `crc32` of the parsed class name and queries `ResolveEntityClass` across registered entity kinds.
```cpp
[[nodiscard]] const Class* ResolveEntityClass(uint8 kind, uint32 crc)
{
if (kind >= ENTITY(Count))
{
return nullptr;
}
const Class* class_ptr = kEntity_type_class_ptr[kind];
if (class_ptr == nullptr)
{
return nullptr;
}
if (class_ptr->CRC != crc)
{
return nullptr;
}
return class_ptr;
}
[[nodiscard]] Class* find_class_by_name(String name)
{
if (!IsValid(name))
{
return nullptr;
}
const uint32 name_crc = crc32(name.Str, name.Size);
for (uint8 kind = 0; kind < ENTITY(Count); ++kind)
{
const Class* class_ptr = ResolveEntityClass(kind, name_crc);
if (class_ptr != nullptr)
{
return const_cast<Class*>(class_ptr);
}
}
return nullptr;
}
```
### 4.4 In-Place Deserialization Algorithm
```
+---------------------------------------------------------------------------------------+
| In-Place Deserialization Flowchart |
+---------------------------------------------------------------------------------------+
| |
| 1. LoadFile(scratch.Arena, filepath) into ByteBuffer |
| | |
| v |
| 2. tokenize_archive(scratch.Arena, file_buffer, &ar.base) |
| | |
| v |
| 3. Read "; class" & Resolve Class* via find_class_by_name(class_name) |
| | |
| v |
| 4. base_ptr = AllocateEntity(manager, class_ptr) |
| | |
| +--> [manager.Entities]: Allocates base Entity |
| +--> [manager.by_type[kind].arena]: Allocates derived struct |
| +--> Mutual Back-Pointers Wired In-Place |
| | |
| v |
| 5. serialize(ar, NonNullPtr<Entity>(base_ptr)) |
| | |
| +--> Streams Base Entity (Entity::kind, version, ID, position) |
| +--> Streams Derived Component (base_ptr->derived_kind, class_version) |
| | |
| v |
| 6. Clear Dirty Flag: base_ptr->is_dirty = false |
| |
+---------------------------------------------------------------------------------------+
```
```cpp
[[nodiscard]] Entity* deserialize_entity_in_place(EntityManager& manager, Archive& ar)
{
Assert(ar.loading);
// 1. Read class name and resolve Class*
String class_name = {};
SERIALIZE(ar, class, class_name);
Class* class_ptr = find_class_by_name(class_name);
if (!class_ptr)
{
return nullptr;
}
// 2. Allocate persistent memory for base and derived components in their respective arenas
Entity* base_ptr = AllocateEntity(manager, class_ptr);
Assert(base_ptr != nullptr);
// 3. Serialize Base Entity and Derived in-place (loads ID and properties from disk)
serialize(ar, NonNullPtr<Entity>(base_ptr));
// 4. Advance generator counter to avoid collisions with loaded IDs
if (base_ptr->ID >= EntityManager::ID)
{
EntityManager::ID = base_ptr->ID + 1;
}
// Freshly loaded entity matches disk state exactly
base_ptr->is_dirty = false;
return base_ptr;
}
```
---
## 5. Entity Deletion Lifecycle & Disk Synchronization (Extracted for Rework)
> [!WARNING]
> **Status: Extracted for Rework**
> The original swap-and-pop in-memory removal logic (`DestroyEntity`, `RemoveDerivedComponent`, and mutual back-pointer fixups) was determined to be overly complex and has been extracted to [`Game/Plans/Entity_Removal_Brainstorm.md`](file:///w:/Classified/Juliet/Game/Plans/Entity_Removal_Brainstorm.md) for further brainstorming and redesign.
>
> Simpler alternative architectures under consideration include:
> - **Active / Tombstone Flag (`is_active` bool):** Retaining entities in-place without moving memory during frame simulation, eliminating pointer invalidation entirely.
> - **Intrusive Free List:** Linking inactive slots via an intrusive linked list to find the first free slot in $O(1)$ without memory shifting.
> - **Generational Handles / Slot Map:** Enabling safe, non-dangling entity references across systems.
> - **Deferred Compaction:** Batch-compacting memory during level loads or scene transitions rather than per-frame swap-and-pop.
---
## 6. Dirty Tracking for Optimal Saves
### 6.1 The Cost of Naive Monolithic & Full-Directory Writes
In a level containing $10{,}000$ entities:
- **Monolithic `world.bin` Save:** Modifying a single entity's $X$ coordinate requires re-serializing all $10{,}000$ entities and overwriting a multi-megabyte binary file. This introduces a huge Git diff and constant merge conflicts.
- **Full-Directory `.jasset` Save:** Iterating through all $10{,}000$ entities and unconditionally writing $10{,}000$ `.jasset` files incurs massive OS file-system overhead (directory table locks, I/O bandwidth) and changes the file timestamps of every asset. Git reports thousands of modified files even when only one entity changed!
### 6.2 The `is_dirty` Flag on `Entity`
To solve this, `Entity` in [`Game/Entity/Entity.h`](file:///w:/Classified/Juliet/Game/Entity/Entity.h#L28-L36) is augmented with an explicit dirty flag:
```cpp
struct Entity final
{
DECLARE_CLASS() // static Class* kind; (Entity's own Class descriptor)
EntityID ID = 0;
Class* derived_kind = nullptr; // Pointer to derived class descriptor (e.g. Inert::kind)
DerivedType derived = nullptr; // Pointer to derived component memory
Vector4 position = {};
bool is_dirty = false;
};
```
### 6.3 Granular State Transitions
The `is_dirty` flag obeys a strict lifecycle state machine:
```
+-----------------------------------+
| Entity Created |
| (AllocateEntity / Editor) |
+-----------------+-----------------+
|
v
+---------------+
+------->|is_dirty: TRUE |<-------+
| +-------+-------+ |
| | |
Entity Mutated | SaveWorld
(Position, Component) | Completed
| v |
| +---------------+ |
+--------+is_dirty: FALSE+--------+
+-------+-------+
^
|
Deserialization
(LoadWorld / Asset)
```
1. **Entity Creation:** Newly spawned entities in the editor have `is_dirty = true`.
2. **Property Mutation:** Any modification to `position` or derived component payload sets `entity->is_dirty = true`.
3. **Successful Deserialization:** Entities loaded from disk initialize with `is_dirty = false`.
4. **Successful Save:** Upon successfully writing an entity to its `.jasset` file, the engine resets `entity->is_dirty = false`.
### 6.4 Version Control Benefits (Git Friendly Assets)
By coupling the one-file-per-entity `.jasset` format with dirty tracking:
- Only modified entities are touched on disk.
- Git status displays only the exact `.jasset` files that were altered by the designer.
- Team members can work concurrently in the same game scene without encountering binary merge conflicts.
### 6.5 Editor Integration (`RenderWorldEditorUI` Hooks)
In [`Game/Data/World.cpp`](file:///w:/Classified/Juliet/Game/Data/World.cpp#L291-L311), editor UI widgets automatically set the dirty flag upon receiving user input:
```cpp
float pos[4] = { ent.position.x, ent.position.y, ent.position.z, ent.position.w };
if (ImGui::DragFloat3("Position", pos, 0.1f))
{
ent.position.x = pos[0];
ent.position.y = pos[1];
ent.position.z = pos[2];
ent.is_dirty = true; // Mark dirty for persistence
UpdateWorld(world);
}
```
---
## 7. Step-by-Step Implementation Roadmap
### Phase 1: Data Structures & Header Definitions
1. **Update `Class.h` & `Entity.h`:**
- Add `using default_init_fct_type = void (*)(void* payload);` and `default_init_fct` to `struct Class` and `MakeClass`.
- Update `DEFINE_ENTITY_VERSIONED` and `DEFINE_CLASS_VERSIONED` to define `default_init_##entity` and pass it to `MakeClass`.
- Add `bool is_dirty = false;` to `struct Entity`.
- Update `MakeEntity<EntityType>` to assign `base_ptr->ID = EntityManager::ID++;` and delegate allocation and defaults cleanly to `AllocateEntity`.
- Declare `[[nodiscard]] const Class* ResolveEntityClass(uint8 kind, uint32 crc);` and `[[nodiscard]] Class* find_class_by_name(String name);` in `Entity.h`.
2. **Update `EntityManager.h`:**
- Declare `[[nodiscard]] Entity* AllocateEntity(EntityManager& manager, Class* class_ptr);`.
- *(Note: `DestroyEntity` and `RemoveDerivedComponent` deferred to `Entity_Removal_Brainstorm.md`)*
3. **Update `World.h`:**
- Add `VectorArena<EntityID, 1024> PendingDeletions;` to `struct World`.
- Update `SaveWorld` and `LoadWorld` signatures to take directory paths.
### Phase 2: Core Memory Allocation & Wiring in `EntityManager.cpp`
1. Implement `AllocateEntity`:
- Enforce parameter assertions.
- Push to `manager.Entities` with `baseTemplate.ID = 0` (unassigned).
- Allocate block in `manager.by_type[kind].arena`.
- Call `derivedClassPtr->default_init_fct(rawMemory)` (or `MemZero` if null) to initialize struct defaults.
- Wire mutual pointers (`base->derived` and `derived->base`).
- Increment `typedArray.count` and initialize `typedArray.array`.
2. *(Deferred for rework)* `DestroyEntity` & `RemoveDerivedComponent` (See [`Game/Plans/Entity_Removal_Brainstorm.md`](file:///w:/Classified/Juliet/Game/Plans/Entity_Removal_Brainstorm.md)).
### Phase 3: In-Place Deserialization & Serialization Pipeline
1. In `Entity.cpp`:
- Implement `ResolveEntityClass` and `find_class_by_name`.
- `serialize(Archive& ar, NonNullPtr<Entity> entity)` handles both base and derived class serialization.
2. In `World.cpp`:
- Implement `serialize_entity_asset(Archive& ar, NonNullPtr<Entity> entity, String filepath)`.
- Implement `deserialize_entity_asset(EntityManager& manager, Archive& ar, String filepath)`.
- In `deserialize_entity_in_place`, advance `EntityManager::ID` past `base_ptr->ID` to prevent ID collisions.
### Phase 4: World Save/Load Pipeline & Disk Deletion
1. In `World.cpp`:
- Implement `ProcessPendingDeletions(World& world, NonNullPtr<Arena> scratchArena)`.
- Implement `SaveWorld(World& world, String worldDirectory)`:
- Process pending deletions.
- Iterate `manager.Entities`, skipping entities where `!entity.is_dirty`.
- Write dirty entities to `.jasset` files and clear `is_dirty`.
- Implement `LoadWorld(World& world, String worldDirectory)`:
- Enumerate `.jasset` files in directory.
- Call `deserialize_entity_asset` for each file.
### Phase 5: Editor Integration
1. In `RenderWorldEditorUI`:
- Hook `ImGui::DragFloat3` and property inspectors to set `is_dirty = true`.
- Hook "Add Entity" button to call `MakeEntity<Inert>(*world.EntityManager, 0.0f, 0.0f, 0.0f)`.
- Hook "Delete Entity" button to call `RemoveWorldEntity(world, selectedEntityId)`.
---
## 8. Unit Testing & Verification Plan
### 8.1 Test Philosophy & Constraints
Following Juliet coding guidelines:
> "When creating a new system framework, make a unit test. To make the unit test we should not modify the framework code for special unit test case."
Testing is isolated in [`Game/UnitTest/WorldUnitTest.cpp`](file:///w:/Classified/Juliet/Game/UnitTest/WorldUnitTest.cpp) and executed during engine initialization in debug builds.
### 8.2 Comprehensive Test Suite (`WorldUnitTest.cpp`)
The test suite validates every guarantee made in this specification:
```cpp
#include <UnitTest/WorldUnitTest.h>
#if JULIET_DEBUG
#include <Core/Common/CoreUtils.h>
#include <Core/Common/serialization.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/HAL/IO/IOStream.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Thread/ThreadContext.h>
#include <Data/World.h>
#include <Entity/Entity.h>
#include <Entity/EntityManager.h>
namespace UnitTest
{
namespace
{
void TestEntityAllocationAndWiring()
{
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] Running TestEntityAllocationAndWiring...");
TempArena tempArena = scratch_begin(nullptr, 0);
World testWorld{};
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager& manager = *testWorld.EntityManager;
// 1. Allocate Inert Entity via AllocateEntity
Entity* base_entity = AllocateEntity(manager, Inert::kind);
Assert(base_entity != nullptr);
Assert(base_entity->ID == 0); // Pure memory allocator leaves ID unassigned (0) until MakeEntity or deserialization
base_entity->ID = EntityManager::ID++;
Assert(base_entity->ID > 0);
Assert(base_entity->derived_kind == Inert::kind);
Assert(base_entity->derived != nullptr);
Assert(base_entity->is_dirty == true);
// 2. Validate mutual back-pointer wiring
auto* derived = reinterpret_cast<entity_template*>(base_entity->derived);
Assert(derived->base == base_entity);
// 3. DownCast verification
Inert* inert = DownCast<Inert>(base_entity);
Assert(inert != nullptr);
Assert(inert->base == base_entity);
// 4. Validate typed array tracking
typed_entity_array& inert_array = manager.by_type[ENTITY(Inert)];
Assert(inert_array.count == 1);
Assert(inert_array.array == derived);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
scratch_end(tempArena);
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] PASSED: TestEntityAllocationAndWiring");
}
void TestInPlaceDeserialization()
{
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] Running TestInPlaceDeserialization...");
TempArena tempArena = scratch_begin(nullptr, 0);
World testWorld{};
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager& manager = *testWorld.EntityManager;
// 1. Create and populate entity
Inert* created_inert = MakeEntity<Inert>(manager, 12.5f, -44.0f, 108.2f);
Assert(created_inert != nullptr);
created_inert->MeshInstance = 42;
Entity* original_base = created_inert->base;
EntityID original_id = original_base->ID;
// 2. Serialize to text archive memory stream
MemoryStream mem_stream = MakeMemoryStream(tempArena.Arena);
Archive save_ar{ .arena = tempArena.Arena, .loading = false, .stream = &mem_stream };
serialize(save_ar, NonNullPtr<Entity>(original_base));
// 3. Clear manager to simulate fresh load
ShutdownEntityManager();
InitEntityManager(&testWorld);
EntityManager& fresh_manager = *testWorld.EntityManager;
// 4. Tokenize and deserialize in-place
Archive load_ar{ .arena = tempArena.Arena, .loading = true };
tokenize_archive(tempArena.Arena, mem_stream.buffer, &load_ar.base);
Entity* loaded_base = deserialize_entity_in_place(fresh_manager, load_ar);
Assert(loaded_base != nullptr);
Assert(loaded_base->ID == original_id);
Assert(EntityManager::ID > original_id); // Counter was advanced past loaded ID to prevent collisions
Assert(loaded_base->position.x == 12.5f);
Assert(loaded_base->position.y == -44.0f);
Assert(loaded_base->position.z == 108.2f);
Assert(loaded_base->is_dirty == false);
Inert* loaded_inert = DownCast<Inert>(loaded_base);
Assert(loaded_inert != nullptr);
Assert(loaded_inert->base == loaded_base);
Assert(loaded_inert->MeshInstance == 42);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
scratch_end(tempArena);
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] PASSED: TestInPlaceDeserialization");
}
void TestDirtyTrackingLifecycle()
{
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] Running TestDirtyTrackingLifecycle...");
TempArena tempArena = scratch_begin(nullptr, 0);
World testWorld{};
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager& manager = *testWorld.EntityManager;
Entity* entity = AllocateEntity(manager, Inert::kind);
Assert(entity->is_dirty == true);
// Simulate save
entity->is_dirty = false;
Assert(entity->is_dirty == false);
// Simulate mutation
entity->position.x += 1.0f;
entity->is_dirty = true;
Assert(entity->is_dirty == true);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
scratch_end(tempArena);
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] PASSED: TestDirtyTrackingLifecycle");
}
} // namespace
void WorldUnitTest()
{
Log(LogLevel::Message, LogCategory::Game, "==================================================");
Log(LogLevel::Message, LogCategory::Game, "Starting Entity Allocation & Lifecycle Unit Tests");
Log(LogLevel::Message, LogCategory::Game, "==================================================");
TestEntityAllocationAndWiring();
TestInPlaceDeserialization();
// TestSwapAndPopPointerFixup(); // Deferred to Entity_Removal_Brainstorm.md
TestDirtyTrackingLifecycle();
Log(LogLevel::Message, LogCategory::Game, "==================================================");
Log(LogLevel::Message, LogCategory::Game, "All Entity Lifecycle Unit Tests PASSED Successfully");
Log(LogLevel::Message, LogCategory::Game, "==================================================");
}
} // namespace UnitTest
#endif
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,934 @@
# Juliet Game Engine: Technical Specification & Implementation Plan
# 04: Entity Templates ("Blueprints / Prefabs") & Delta Inheritance
- **Document ID**: ENG-PLAN-04
- **Component**: Game Architecture / Entity Component System / Asset Pipeline / Editor
- **Author**: Senior Engine Architect
- **Status**: Draft / Approved for Implementation
- **Target Engine**: Juliet Engine (Milestone: 3D Platformer / World System)
- **Target File**: `w:\Classified\Juliet\Game\Plans\04_Entity_Templates_And_Inheritance.md`
---
## 1. Executive Summary & Architecture Goals
### 1.1 Motivation & Context
As the Juliet engine evolves toward supporting complex interactive worlds, populating scenes by manually constructing raw entity memory from hardcoded values or monolithic binary streams (`world.bin`) creates severe scalability bottlenecks. Designers and environment artists require reusable entity archetypes—such as foliage, dynamic destructibles, hazards, enemies, and interactive props—that can be authored once and instantiated thousands of times with custom overrides.
In modern commercial engines, this pattern is foundational:
- **Unreal Engine**: Blueprint classes (`.uasset`) serve as class archetypes, instanced as Actors with per-instance component property overrides.
- **Unity**: Prefabs (`.prefab`) serve as asset archetypes, instanced in scenes with explicit serialized property modification lists.
In the **Juliet Game Engine**, this feature is formalized as **Entity Templates**.
### 1.2 Architectural Goals
1. **Unified File Format (`.jasset`)**:
Templates and world entity instances must share the exact same human-readable, version-control-friendly Key-Value (KV) file format. A template is simply an entity `.jasset` file located within `Assets/Templates/`, while a world entity is a `.jasset` file located within `Assets/Worlds/<WorldName>/Entities/` that references a parent template via metadata.
2. **Zero Format Duplication**:
There must be no separate "prefab file format" versus "entity instance file format". The same serialization/deserialization code paths parse both archetypes and instances, completely eliminating format divergence, schema version desynchronization, and redundant parsing logic.
3. **Delta Property Overrides (Sparse Inheritance)**:
Instances only serialize the fields that intentionally deviate from their template archetype. Any property omitted in the instance `.jasset` retains the exact bitwise value defined by the template archetype.
4. **Zero Runtime Inheritance Overhead**:
No virtual table lookups, no runtime inheritance trees, and no pointer chasing at tick time. At instantiation time, the template archetype's memory footprint is blitted into contiguous engine arrays (`EntityManager::by_type`), and instance delta overrides are parsed directly into that memory block. Once instantiated, an entity created from a template executes with identical CPU cache locality and zero performance penalty compared to a hardcoded entity.
5. **Arena-Centric Memory Model**:
All template assets are loaded into a dedicated, isolated `TemplateArena`. World entity instances allocate their runtime state out of `World::WorldArena` and `EntityManager::by_type[kind].arena`. Scratch computations during parsing utilize thread-local scratch arenas (`scratch_begin` / `scratch_end`). No heap allocation (`malloc`, `calloc`, `new`) is permitted.
6. **Strict Engine Conformance**:
- Zero exceptions (`noexcept` by design).
- Strict warning compliance (`-Wall -Wextra -Werror` / `/W4 /WX`).
- Strict explicit casting (`static_cast`, `reinterpret_cast`; C-style casts strictly prohibited).
- Universal `[[nodiscard]]` on all value-returning queries and allocators.
- Consistent naming (`CamelCase` for types, functions, and member variables).
- Comprehensive precondition assertions via `Assert`.
- Mandatory curly braces `{}` across all control flow statements.
---
## 2. The Template Linking Mechanism
### 2.1 File Format Specification (`.jasset`)
All templates and entities in Juliet use the `.jasset` text format. The format consists of:
- **Comment Lines**: Lines starting with `#` or `//` are treated as comments and ignored.
- **Metadata Directives**: Lines starting with `; ` represent engine-level structural metadata (e.g., entity type, template references, versioning).
- **Key-Value Pairs**: Key name followed by a colon and a space (`<Key>: <Value>`).
```ini
# ==============================================================================
# Assets/Templates/RockLarge.jasset
# Entity Archetype: Large Mossy Rock
# ==============================================================================
; entity_type: Inert
Position: 0.0, 0.0, 0.0
MeshAsset: Assets/Meshes/Rock_01.obj
Scale: 1.0, 1.0, 1.0
Mass: 250.0
IsDestructible: true
Health: 100.0
```
### 2.2 The `; template` Directive
When an entity instance is authored for a world, it specifies its archetype via the `; template` directive:
```ini
# ==============================================================================
# Assets/Worlds/Level01/Entities/RockLarge_042.jasset
# Instance: Rock Large #042 in Level 01
# ==============================================================================
; template: Assets/Templates/RockLarge.jasset
Position: 142.5, 12.0, -84.2
Scale: 1.4, 1.4, 1.4
Health: 50.0
```
Notice the power of sparse delta overrides in this format:
- `Position` is set to the instance's unique world coordinates.
- `Scale` is enlarged to $1.4\times$.
- `Health` is damaged down to $50.0$.
- Properties omitted—`MeshAsset`, `Mass`, and `IsDestructible`—are not duplicated in the file. They automatically inherit the authoritative values from `RockLarge.jasset`.
### 2.3 Relative Path vs CRC Identifier
To balance human-readability in source control with blazing runtime lookup speeds:
- **Asset Authoring & Storage**: Files store canonical workspace-relative paths (e.g., `Assets/Templates/RockLarge.jasset`).
- **Runtime Representation**: The engine computes a 32-bit CRC (`crc32`) of the normalized relative path string.
- Runtime lookup into `EntityTemplateCache` operates via `uint32 TemplateCrc`.
- In `JULIET_DEBUG` builds, the original `String TemplatePath` is retained within the cached struct for diagnostic logging, error messages, and inspector UI display.
```cpp
constexpr uint32 kInvalidTemplateCrc = 0;
[[nodiscard]] inline uint32 HashTemplatePath(String path)
{
Assert(IsValid(path));
return crc32(path.Str, path.Size);
}
```
### 2.4 Standalone Entities vs Templated Instances
The engine architecture seamlessly unifies two entity categories:
| Entity Category | `; template` Directive Present? | Memory Initialization Source | Primary Use Case |
| :--- | :--- | :--- | :--- |
| **Standalone Entity** | **No** | Zero-initialized memory block (`ArenaPushStruct` / `MemSet`). All properties must be specified in the instance `.jasset`. | Unique, one-off actors (e.g., Level Script Trigger, Primary Player Spawn Marker, Boss Controller). |
| **Templated Instance** | **Yes** | Blitted directly from `CachedTemplate::DefaultDerivedMemory`. Instance `.jasset` only specifies delta property overrides. | Reusable archetypes (e.g., environmental props, foliage, pickups, enemy minions, projectiles). |
---
## 3. In-Engine Template Caching
### 3.1 Memory Layout & `TemplateArena` Isolation
Reading and parsing text files from storage is orders of magnitude slower than memory copying. In a world containing 10,000 instances of `GrassClump` and 2,000 instances of `RockLarge`, disk I/O and text tokenization must occur **exactly once per archetype**.
To achieve zero memory fragmentation and eliminate dynamic heap allocations:
1. The engine instantiates an isolated `TemplateArena` at subsystem startup.
2. When a template is requested, the system checks the `EntityTemplateCache`.
3. If not cached, the template `.jasset` file is loaded from disk into temporary scratch memory, parsed, and baked into a contiguous binary archetype memory snapshot inside `TemplateArena`.
4. The parsed archetype snapshot remains resident in `TemplateArena` for the lifetime of the application or world session.
```
+---------------------------------------------------------------------------------+
| TemplateArena |
+---------------------------------------------------------------------------------+
| [CachedTemplate #0] |
| - TemplateCrc: 0x9A4B12F0 ("Assets/Templates/RockLarge.jasset") |
| - EntityKind: Pointer to Inert::Kind |
| - DefaultBase: { ID=0, Kind=Inert::Kind, Derived=nullptr, X=0, Y=0, Z=0 } |
| - DefaultDerivedMemory: [ sizeof(Inert) binary snapshot: MeshInstance=... ] |
+---------------------------------------------------------------------------------+
| [CachedTemplate #1] |
| - TemplateCrc: 0x4D2E88C1 ("Assets/Templates/CoinPickup.jasset") |
| - EntityKind: Pointer to Collectible::Kind |
| - DefaultBase: { ID=0, Kind=Collectible::Kind, ... } |
| - DefaultDerivedMemory: [ sizeof(Collectible) binary snapshot: Value=100 ] |
+---------------------------------------------------------------------------------+
| ... Free Arena Capacity for Additional Archetypes ... |
+---------------------------------------------------------------------------------+
```
### 3.2 Data Structures
The template caching infrastructure is defined with explicit, strictly-typed C++ structures adhering to Juliet guidelines:
```cpp
#pragma once
#include <Core/Common/CoreTypes.h>
#include <Core/Common/CoreUtils.h>
#include <Core/Common/NonNullPtr.h>
#include <Core/Common/String.h>
#include <Core/Container/Vector.h>
#include <Core/Memory/MemoryArena.h>
#include <Engine/Class.h>
#include <Entity/Entity.h>
struct CachedTemplate
{
uint32 TemplateCrc = 0;
Class* EntityKind = nullptr;
Entity DefaultBase = {};
void* DefaultDerivedMemory = nullptr;
size_t DerivedMemorySize = 0;
#if JULIET_DEBUG
String SourcePath = {};
#endif
};
constexpr size_t kMaxCachedTemplates = 1024;
struct EntityTemplateCache
{
Arena* CacheArena = nullptr;
VectorArena<CachedTemplate, kMaxCachedTemplates> Templates;
};
// Subsystem API
[[nodiscard]] EntityTemplateCache* InitEntityTemplateCache(NonNullPtr<Arena> parentArena);
void ShutdownEntityTemplateCache(NonNullPtr<EntityTemplateCache> cache);
[[nodiscard]] CachedTemplate* GetOrLoadTemplate(NonNullPtr<EntityTemplateCache> cache, String relativePath);
[[nodiscard]] CachedTemplate* FindCachedTemplate(NonNullPtr<EntityTemplateCache> cache, uint32 templateCrc);
void InvalidateTemplateCache(NonNullPtr<EntityTemplateCache> cache);
```
### 3.3 Loading Pipeline: Disk to Archetype Memory Snapshot
When `GetOrLoadTemplate` is invoked with a relative path:
1. **Hash & Probe**: Compute `crc32` of `relativePath`. Search `cache->Templates` for an existing entry. If found, immediately return the cached pointer ($O(1)$ amortized).
2. **Scratch Allocation**: Open a scratch arena frame (`TempArena scratch = scratch_begin(nullptr, 0);`).
3. **Disk I/O**: Resolve the path via `GetAssetPath(scratch.Arena, relativePath)` and load the entire file into a raw byte buffer via `LoadFile(scratch.Arena, fullPath)`.
- Precondition check: `Assert(fileBuffer.Data != nullptr);`
4. **Header Parse**: Scan the file buffer for `; entity_type: <TypeName>`. Look up the corresponding `Class*` via the global entity registry (`kEntity_type_class_ptr`).
5. **Archetype Memory Allocation**: Allocate `DefaultDerivedMemory` directly out of `cache->CacheArena`:
```cpp
void* archetypeMem = ArenaPushSize(cache->CacheArena,
entityClass->size_of,
entityClass->alignment,
true JULIET_DEBUG_PARAM("CachedTemplateDerived"));
```
6. **Default Base Setup**: Initialize a local `Entity defaultBase`:
```cpp
Entity defaultBase = {};
defaultBase.derived_kind = entityClass;
defaultBase.derived = archetypeMem;
```
7. **Back-Pointer Linking**: Set `entity_template::base` in the archetype memory:
```cpp
auto* archetypeTemplate = reinterpret_cast<entity_template*>(archetypeMem);
archetypeTemplate->base = &cachedEntry->DefaultBase;
```
8. **KV Property Parsing**: Parse all key-value pairs in the template `.jasset` file and write their deserialized values directly into `archetypeMem` and `defaultBase`.
9. **Cache Insertion**: Store the fully baked `CachedTemplate` record in `cache->Templates`.
10. **Scratch Release**: Release the temporary file buffer (`scratch_end(scratch);`).
---
## 4. Instantiation & Delta Property Overrides
### 4.1 Memory Architecture: Base vs. Derived Entities
In Juliet, an entity is split into two tightly coupled structures:
1. **`Entity` (Base)**: Contains universal spatial and lifecycle fields:
```cpp
struct Entity final
{
DECLARE_CLASS()
EntityID ID = 0;
Class* derived_kind = nullptr;
DerivedType derived = nullptr; // Points to the specialized struct
Vector4 position = {};
bool is_dirty = false;
};
```
2. **`Derived` (Specialized Type)**: e.g., `Inert`, `Collectible`, `Player`. The first member is always `DECLARE_ENTITY()`, which expands to:
```cpp
Entity* base; // Back-pointer to the base Entity
DECLARE_CLASS() // static Class* kind;
```
Because `DerivedType` stores a back-pointer (`base`) to `Entity`, **a shallow memory copy of an archetype invalidates this pointer**! The loading pipeline must explicitly restore this invariant.
### 4.2 The 5-Step Instantiation Pipeline
When an entity instance is spawned or deserialized from a `.jasset` file, the engine executes this strict 5-step sequence:
```
[ Step 1: Pre-allocate ]
- manager.Entities.PushBack(Entity{})
- ArenaPushSize(manager.by_type[Kind].arena)
|
v
[ Step 2: Copy Archetype Memory ]
- MemCopy(instanceDerivedMem, template->DefaultDerivedMemory, size_of)
- instanceBase->X/Y/Z = template->DefaultBase.X/Y/Z
|
v
[ Step 3: Fixup Base Back-Pointer ] <--- CRITICAL ARCHITECTURAL STEP
- instanceDerived->Base = instanceBase
- instanceBase->Derived = instanceDerived
|
v
[ Step 4: Parse Instance KV Delta ]
- Scan instance .jasset tokens
- Match each key to struct property offset
- Overwrite only specified properties
|
v
[ Step 5: Finalize & Post-Init ]
- Assign Unique EntityID
- Register with Render/Physics systems
```
#### Step-by-Step Code Execution
```cpp
[[nodiscard]] Entity* InstantiateEntityFromTemplate(EntityManager& manager,
NonNullPtr<const CachedTemplate> templateArchetype,
NonNullPtr<Arena> scratchArena,
String instanceKvContent)
{
Assert(templateArchetype->EntityKind != nullptr);
Assert(templateArchetype->DefaultDerivedMemory != nullptr);
Class* entityKind = templateArchetype->EntityKind;
const size_t derivedSize = entityKind->size_of;
const size_t derivedAlign = entityKind->alignment;
// -------------------------------------------------------------------------
// STEP 1: Pre-allocate instance in EntityManager
// -------------------------------------------------------------------------
Entity baseEntity = {};
baseEntity.ID = EntityManager::ID++;
baseEntity.Kind = entityKind;
// Push into flat base vector
manager.Entities.PushBack(baseEntity);
Entity* instanceBase = manager.Entities.Back();
Assert(instanceBase != nullptr);
// Allocate memory in the typed array arena
auto* instanceDerived = reinterpret_cast<entity_template*>(
ArenaPushSize(manager.by_type[entityKind->kind].arena,
derivedSize,
derivedAlign,
false JULIET_DEBUG_PARAM(kEntity_type_names[entityKind->kind]))
);
Assert(instanceDerived != nullptr);
// Track array head if first element
if (manager.by_type[entityKind->kind].array == nullptr)
{
manager.by_type[entityKind->kind].array = instanceDerived;
}
manager.by_type[entityKind->kind].count += 1;
// -------------------------------------------------------------------------
// STEP 2: Copy cached template defaults into instance derived memory
// -------------------------------------------------------------------------
MemCopy(instanceDerived, templateArchetype->DefaultDerivedMemory, derivedSize);
// Inherit base spatial defaults
instanceBase->X = templateArchetype->DefaultBase.X;
instanceBase->Y = templateArchetype->DefaultBase.Y;
instanceBase->Z = templateArchetype->DefaultBase.Z;
// -------------------------------------------------------------------------
// STEP 3: Ensure derived back-pointer points to THIS instance's base
// -------------------------------------------------------------------------
instanceDerived->Base = instanceBase;
instanceBase->Derived = instanceDerived;
// -------------------------------------------------------------------------
// STEP 4: Parse instance .jasset KV nodes over the memory
// -------------------------------------------------------------------------
if (IsValid(instanceKvContent))
{
ApplyKvDeltaOverrides(instanceBase, instanceDerived, entityKind, instanceKvContent);
}
// -------------------------------------------------------------------------
// STEP 5: Post-Instantiation Initialization
// -------------------------------------------------------------------------
// If the entity is an Inert mesh, update its graphics transform
if (entityKind->kind == ENTITY(Inert))
{
auto* inert = reinterpret_cast<Inert*>(instanceDerived);
if (inert->MeshInstance != indexMax)
{
SetMeshInstanceTransform(inert->MeshInstance,
MatrixTranslation(instanceBase->X, instanceBase->Y, instanceBase->Z));
}
}
return instanceBase;
}
```
### 4.3 Key-Value Parsing & Reflection Binding
To apply delta overrides, the engine maps parsed string keys to memory offsets. Juliet utilizes lightweight property reflection metadata registered on each `Class`:
```cpp
enum class PropertyType : uint8
{
Float,
Int32,
Bool,
Vector3,
MeshAsset,
String
};
struct PropertyDescriptor
{
String Name;
size_t Offset;
PropertyType Type;
bool IsBaseProperty; // True if located on Entity, false if on Derived
};
void ApplyKvDeltaOverrides(Entity* base, void* derived, Class* cls, String kvContent)
{
Assert(base != nullptr);
Assert(derived != nullptr);
Assert(cls != nullptr);
TempArena scratch = scratch_begin(nullptr, 0);
KvParser parser = InitKvParser(kvContent);
KvPair pair = {};
while (NextKvPair(&parser, &pair))
{
// Directives like '; template' or '; entity_type' are skipped
if (pair.Key.Size > 0 && pair.Key.Str[0] == ';')
{
continue;
}
// Check base properties first
if (StringCompare(pair.Key, ConstString("Position")) == 0 || StringCompare(pair.Key, ConstString("position")) == 0)
{
Vector3 pos = ParseVector3(pair.Value);
base->position.x = pos.x;
base->position.y = pos.y;
base->position.z = pos.z;
continue;
}
// Look up property in Class reflection table
const PropertyDescriptor* prop = FindPropertyDescriptor(cls, pair.Key);
if (prop != nullptr)
{
void* targetField = static_cast<uint8*>(derived) + prop->Offset;
switch (prop->Type)
{
case PropertyType::Float:
{
*reinterpret_cast<float*>(targetField) = ParseFloat(pair.Value);
break;
}
case PropertyType::Int32:
{
*reinterpret_cast<int32*>(targetField) = ParseInt32(pair.Value);
break;
}
case PropertyType::Bool:
{
*reinterpret_cast<bool*>(targetField) = ParseBool(pair.Value);
break;
}
case PropertyType::MeshAsset:
{
String meshPath = TrimWhitespace(pair.Value);
MeshAssetID meshId = LoadMesh(meshPath);
*reinterpret_cast<MeshAssetID*>(targetField) = meshId;
break;
}
default:
{
break;
}
}
}
}
scratch_end(scratch);
}
```
---
## 5. Editor Workflow & Operations (Romeo / ImGui)
### 5.1 "Create Template from Entity" Workflow
An artist or designer often crafts an intricate entity in the active level (configuring mesh, collider, and scale) and decides it should become a reusable archetype.
#### Sequence Diagram / Workflow:
1. **User Action**: Right-click an entity in the Romeo World Editor Outliner $\rightarrow$ Select *"Convert to Template..."*.
2. **Modal Dialog**: The editor prompts for the template asset name (e.g., `SpikeTrap_Large`).
3. **Sanitize World Transform**:
- The world position (`X, Y, Z`) is sanitized to origin (`0.0, 0.0, 0.0`) for the template asset.
- Rotations and local scale are preserved.
4. **Serialize Archetype**:
- Write `Assets/Templates/SpikeTrap_Large.jasset` containing:
- `; entity_type: <KindName>`
- All authored property values.
5. **Convert Live Instance**:
- The selected world entity is transformed into an instance of the newly created template.
- The entity is assigned the template CRC: `entity->TemplateCrc = crc32("Assets/Templates/SpikeTrap_Large.jasset")`.
- When the world is saved, this entity serializes as a sparse delta referencing the template!
```cpp
#if JULIET_EDITOR
bool CreateTemplateFromEntity(World& world,
size_t entityIndex,
String templateName,
NonNullPtr<Arena> scratchArena)
{
auto& manager = *world.EntityManager;
Assert(entityIndex < manager.Entities.Size());
Entity* sourceEntity = &manager.Entities[entityIndex];
Class* entityKind = sourceEntity->derived_kind;
void* derivedMem = sourceEntity->derived;
// Format destination template path
String templatePath = Format(scratchArena, "Assets/Templates/{}.jasset", CStr(templateName));
String fullDiskPath = GetAssetPath(scratchArena, templatePath);
// Open IOStream for write
IOStream* fileStream = IOFromFile(scratchArena, fullDiskPath, ConstString("wb"));
if (fileStream == nullptr)
{
LogError(LogCategory::Game, "Failed to open file for template creation: %s", CStr(fullDiskPath));
return false;
}
// Write Header Directive
IOPrintf(fileStream, "; entity_type: %s\n\n", kEntity_type_names[entityKind->kind]);
// Write Origin Position
IOPrintf(fileStream, "Position: 0.0, 0.0, 0.0\n");
// Write Derived Properties via Reflection Table
SerializeDerivedPropertiesToKv(fileStream, entityKind, derivedMem);
IOClose(fileStream);
// Register with in-engine cache immediately
auto* templateCache = GetGameState()->TemplateCache;
if (templateCache != nullptr)
{
(void)GetOrLoadTemplate(templateCache, templatePath);
}
LogMessage(LogCategory::Game, "Successfully created template: %s", CStr(templatePath));
return true;
}
#endif
```
### 5.2 "Spawn Instance from Template" Workflow
1. In the Romeo Content Browser, browse `Assets/Templates/`.
2. Drag a `.jasset` template into the 3D viewport, or click *"Spawn Template"* in the World Editor toolbar.
3. The viewport raycasts against the collision mesh/floor to compute `hitPosition`.
4. The editor calls:
```cpp
CachedTemplate* archetype = GetOrLoadTemplate(templateCache, templatePath);
Entity* newInstance = InstantiateEntityFromTemplate(manager, archetype, scratchArena, {});
newInstance->X = hitPosition.X;
newInstance->Y = hitPosition.Y;
newInstance->Z = hitPosition.Z;
```
5. The entity is immediately live, selectable, and rendered.
### 5.3 Inspector Delta Highlighting & Property Diffing
To make template inheritance intuitive, the inspector visually flags overridden properties:
- **Default Property**: Rendered in standard gray text.
- **Overridden Property**: Rendered in **bold bright cyan** with an undo/revert icon button `[R]`.
```cpp
#if JULIET_EDITOR
void RenderEntityPropertyInspector(Entity* entity, CachedTemplate* archetype)
{
Assert(entity != nullptr);
const bool isTemplated = (archetype != nullptr);
ImGui::Text("Entity ID: %llu", entity->ID);
if (isTemplated)
{
ImGui::TextColored(ImVec2(0.4f, 0.8f, 1.0f, 1.0f), "Template: %s", archetype->SourcePath.Str);
ImGui::SameLine();
if (ImGui::SmallButton("Revert All to Template"))
{
RevertEntityToTemplate(entity, archetype);
}
ImGui::Separator();
}
// Iterate through properties
Class* cls = entity->derived_kind;
for (size_t i = 0; i < cls->PropertyCount; ++i)
{
const PropertyDescriptor& prop = cls->Properties[i];
void* instanceField = static_cast<uint8*>(entity->derived) + prop.Offset;
void* templateField = isTemplated ? (static_cast<uint8*>(archetype->DefaultDerivedMemory) + prop.Offset) : nullptr;
const bool isOverridden = isTemplated && (MemCompare(instanceField, templateField, GetPropertySize(prop.Type)) != 0);
if (isOverridden)
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec2(0.2f, 1.0f, 1.0f, 1.0f));
}
// Render editor widget (DragFloat, InputText, etc.)
RenderPropertyWidget(prop, instanceField);
if (isOverridden)
{
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::PushID(static_cast<int>(i));
if (ImGui::SmallButton("R"))
{
// Revert this single property
MemCopy(instanceField, templateField, GetPropertySize(prop.Type));
}
ImGui::PopID();
if (ImGui::IsItemHovered())
{
ImGui::SetTooltip("Revert property to template default");
}
}
}
}
#endif
```
### 5.4 "Revert to Template" Mechanics
Reverting an entire entity to its template archetype restores all properties while strictly preserving world positioning and the instance's unique `EntityID`:
```cpp
void RevertEntityToTemplate(NonNullPtr<Entity> instance, NonNullPtr<const CachedTemplate> archetype)
{
Assert(instance->derived_kind == archetype->EntityKind);
Assert(archetype->DefaultDerivedMemory != nullptr);
void* derivedMem = instance->derived;
const size_t derivedSize = archetype->EntityKind->size_of;
// Preserve the current Base pointer
Entity* basePtr = instance.Get();
// 1. Re-copy the archetype defaults
MemCopy(derivedMem, archetype->DefaultDerivedMemory, derivedSize);
// 2. Re-establish the Base back-pointer
auto* templateDerived = reinterpret_cast<entity_template*>(derivedMem);
templateDerived->base = basePtr;
// 3. Mark visual / physics state as updated
if (instance->derived_kind->kind == ENTITY(Inert))
{
auto* inert = reinterpret_cast<Inert*>(derivedMem);
if (inert->MeshInstance != indexMax)
{
SetMeshInstanceTransform(inert->MeshInstance,
MatrixTranslation(basePtr->position.x, basePtr->position.y, basePtr->position.z));
}
}
}
```
---
## 6. Step-by-Step Implementation Roadmap
```
+-----------------------------------------------------------------------------+
| Phase 1: Core KV Parser & File Serialization Architecture |
| - Fast, zero-allocation Key-Value streaming parser |
| - Property reflection table integration on Class struct |
+-----------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------+
| Phase 2: Template Cache Subsystem (`EntityTemplateCache`) |
| - Dedicated TemplateArena initialization |
| - Archetype loading, CRC hashing, and resident memory snapshot baking |
+-----------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------+
| Phase 3: Instantiation & Delta Overrides in `EntityManager` |
| - Implement 5-Step Instantiation Pipeline |
| - Derived back-pointer fixup validation |
| - Sparse delta serialization for world saving |
+-----------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------+
| Phase 4: Romeo Editor UI & Inspector Workflow |
| - Template browser window in ImGui |
| - "Create Template from Entity" context menu |
| - Inspector delta highlighting and per-property "Revert" action |
+-----------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------+
| Phase 5: Production Verification & Non-Invasive Unit Testing Suite |
| - Comprehensive test cases for cache, delta inheritance, and memory safety |
+-----------------------------------------------------------------------------+
```
### Detailed Milestone Tasks
| Phase | Target Files | Objective / Deliverable | Success Criteria |
| :--- | :--- | :--- | :--- |
| **Phase 1** | `Juliet/include/Engine/KvParser.h`<br>`Juliet/src/Engine/KvParser.cpp`<br>`Juliet/include/Engine/Class.h` | Build a zero-allocation streaming Key-Value parser operating entirely over `String` slices and `Arena*`. Add property descriptor tables to `Class`. | Parses full `.jasset` buffer in $<50\mu\text{s}$ without dynamic allocations. Zero exceptions. |
| **Phase 2** | `Game/Entity/TemplateCache.h`<br>`Game/Entity/TemplateCache.cpp` | Implement `EntityTemplateCache`, `CachedTemplate`, and on-demand disk loader backed by `TemplateArena`. | Repeated loads of same template return cached pointer in $O(1)$ time without disk reads. |
| **Phase 3** | `Game/Entity/EntityManager.h`<br>`Game/Entity/EntityManager.cpp`<br>`Game/Data/World.cpp` | Implement `InstantiateEntityFromTemplate` using the 5-step pipeline. Update world save/load to serialize sparse deltas when `; template` is present. | Instantiated entities correctly retain archetype defaults while applying deltas. `Base` pointer is guaranteed valid. |
| **Phase 4** | `Game/Data/World.cpp`<br>`Game/Debug/WorldEditorUI.cpp` | Add ImGui widgets for template authoring, template instantiation drag-and-drop, delta highlighting, and "Revert" actions. | Designer can convert an entity to a template and revert modified properties with instant visual update. |
| **Phase 5** | `Game/UnitTest/TemplateUnitTest.h`<br>`Game/UnitTest/TemplateUnitTest.cpp` | Author non-invasive unit tests validating KV parsing, caching, delta override correctness, back-pointer fixup, and revert operations. | 100% test pass rate with zero memory leaks and all assertions satisfied under `/WX`. |
---
## 7. Production-Grade Unit Testing Plan
### 7.1 Testing Philosophy & Non-Invasive Framework Rules
In accordance with Juliet engine guidelines:
- **No framework pollution**: Unit tests must not introduce test-only `#ifdef` branches or dummy parameters into production engine systems.
- **Isolated test arenas**: All tests create their own temporary scratch arena or sub-arena and release it upon completion.
- **Deterministic verification**: Every assumption (data alignment, pointer fixups, delta override values) is verified through explicit `Assert` statements.
### 7.2 Header Specification (`Game/UnitTest/TemplateUnitTest.h`)
```cpp
#pragma once
#include <Juliet.h>
#if JULIET_DEBUG
namespace UnitTest
{
void RunTemplateAndInheritanceUnitTests();
}
#endif
```
### 7.3 Complete Unit Test Suite Implementation (`Game/UnitTest/TemplateUnitTest.cpp`)
```cpp
#include <UnitTest/TemplateUnitTest.h>
#if JULIET_DEBUG
#include <Core/Common/CoreUtils.h>
#include <Core/Logging/LogManager.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Thread/ThreadContext.h>
#include <Data/World.h>
#include <Entity/Entity.h>
#include <Entity/EntityManager.h>
#include <Entity/TemplateCache.h>
namespace UnitTest
{
// =========================================================================
// Test 1: Key-Value Parser Verification
// =========================================================================
static void TestKvParser(NonNullPtr<Arena> testArena)
{
LogMessage(LogCategory::Game, "[UnitTest] Starting TestKvParser...");
const char* sampleKv =
"; entity_type: Inert\n"
"# This is a comment\n"
"Position: 10.5, -20.0, 30.25\n"
"Scale: 2.0, 2.0, 2.0\n"
"MeshAsset: Assets/Meshes/Cube.obj\n"
"IsActive: true\n";
KvParser parser = InitKvParser(WrapString(sampleKv));
KvPair pair = {};
// 1. Directive
Assert(NextKvPair(&parser, &pair));
Assert(StringCompare(pair.Key, ConstString("; entity_type")) == 0);
Assert(StringCompare(pair.Value, ConstString("Inert")) == 0);
// 2. Position
Assert(NextKvPair(&parser, &pair));
Assert(StringCompare(pair.Key, ConstString("Position")) == 0);
Vector3 pos = ParseVector3(pair.Value);
Assert(pos.X == 10.5f);
Assert(pos.Y == -20.0f);
Assert(pos.Z == 30.25f);
// 3. Scale
Assert(NextKvPair(&parser, &pair));
Assert(StringCompare(pair.Key, ConstString("Scale")) == 0);
// 4. MeshAsset
Assert(NextKvPair(&parser, &pair));
Assert(StringCompare(pair.Key, ConstString("MeshAsset")) == 0);
Assert(StringCompare(pair.Value, ConstString("Assets/Meshes/Cube.obj")) == 0);
// 5. IsActive
Assert(NextKvPair(&parser, &pair));
Assert(StringCompare(pair.Key, ConstString("IsActive")) == 0);
Assert(ParseBool(pair.Value) == true);
// End of stream
Assert(!NextKvPair(&parser, &pair));
LogMessage(LogCategory::Game, "[UnitTest] TestKvParser PASSED.");
}
// =========================================================================
// Test 2: Template Cache & Single-Load Invariant
// =========================================================================
static void TestTemplateCache(NonNullPtr<Arena> testArena)
{
LogMessage(LogCategory::Game, "[UnitTest] Starting TestTemplateCache...");
EntityTemplateCache* cache = InitEntityTemplateCache(testArena);
Assert(cache != nullptr);
String templatePath = ConstString("Assets/Templates/TestRock.jasset");
// First load: loads and caches
CachedTemplate* firstLoad = GetOrLoadTemplate(cache, templatePath);
Assert(firstLoad != nullptr);
Assert(firstLoad->TemplateCrc == HashTemplatePath(templatePath));
Assert(firstLoad->EntityKind == Inert::Kind);
// Second load: must return identical cached pointer without re-allocating
CachedTemplate* secondLoad = GetOrLoadTemplate(cache, templatePath);
Assert(secondLoad == firstLoad);
// Verify lookup by CRC
CachedTemplate* crcLookup = FindCachedTemplate(cache, firstLoad->TemplateCrc);
Assert(crcLookup == firstLoad);
ShutdownEntityTemplateCache(cache);
LogMessage(LogCategory::Game, "[UnitTest] TestTemplateCache PASSED.");
}
// =========================================================================
// Test 3: 5-Step Instantiation Pipeline & Derived Back-Pointer Fixup
// =========================================================================
static void TestInstantiationPipeline(NonNullPtr<Arena> testArena)
{
LogMessage(LogCategory::Game, "[UnitTest] Starting TestInstantiationPipeline...");
// Setup mock World and EntityManager
World world = {};
InitWorld(&world, testArena);
InitEntityManager(&world);
auto& manager = *world.EntityManager;
// Construct a synthetic template archetype
CachedTemplate mockTemplate = {};
mockTemplate.TemplateCrc = 0xABCD1234;
mockTemplate.EntityKind = Inert::Kind;
mockTemplate.DefaultBase.X = 1.0f;
mockTemplate.DefaultBase.Y = 2.0f;
mockTemplate.DefaultBase.Z = 3.0f;
Inert defaultInert = {};
defaultInert.MeshInstance = 42; // Template default
mockTemplate.DefaultDerivedMemory = &defaultInert;
mockTemplate.DerivedMemorySize = sizeof(Inert);
// Delta content overrides Position and leaves MeshInstance unspecified
String instanceKv = ConstString("Position: 100.0, 200.0, 300.0\n");
Entity* instance = InstantiateEntityFromTemplate(manager, &mockTemplate, testArena, instanceKv);
Assert(instance != nullptr);
// Verify Step 1 & 2: Base spatial delta applied, non-overridden derived property retained
Assert(instance->position.x == 100.0f);
Assert(instance->position.y == 200.0f);
Assert(instance->position.z == 300.0f);
auto* inertDerived = DownCast<Inert>(instance);
Assert(inertDerived != nullptr);
Assert(inertDerived->MeshInstance == 42); // Retained from template!
// Verify Step 3: CRITICAL back-pointer fixup check
Assert(inertDerived->base == instance);
Assert(instance->derived == inertDerived);
ShutdownEntityManager();
ShutdownWorld(&world);
LogMessage(LogCategory::Game, "[UnitTest] TestInstantiationPipeline PASSED.");
}
// =========================================================================
// Test 4: Delta Override & Revert Functionality
// =========================================================================
static void TestDeltaOverrideAndRevert(NonNullPtr<Arena> testArena)
{
LogMessage(LogCategory::Game, "[UnitTest] Starting TestDeltaOverrideAndRevert...");
World world = {};
InitWorld(&world, testArena);
InitEntityManager(&world);
auto& manager = *world.EntityManager;
CachedTemplate mockTemplate = {};
mockTemplate.TemplateCrc = 0x11223344;
mockTemplate.EntityKind = Inert::Kind;
mockTemplate.DefaultBase.X = 0.0f;
mockTemplate.DefaultBase.Y = 0.0f;
mockTemplate.DefaultBase.Z = 0.0f;
Inert defaultInert = {};
defaultInert.MeshInstance = 100;
mockTemplate.DefaultDerivedMemory = &defaultInert;
mockTemplate.DerivedMemorySize = sizeof(Inert);
// Instantiate with delta
String instanceKv = ConstString("MeshInstance: 999\nPosition: 5.0, 5.0, 5.0\n");
Entity* instance = InstantiateEntityFromTemplate(manager, &mockTemplate, testArena, instanceKv);
auto* inertDerived = DownCast<Inert>(instance);
Assert(inertDerived->MeshInstance == 999); // Delta applied
// Execute Revert to Template
RevertEntityToTemplate(instance, &mockTemplate);
// Verify property restored to template default
Assert(inertDerived->MeshInstance == 100);
// Verify world position is preserved across revert
Assert(instance->position.x == 5.0f);
Assert(instance->position.y == 5.0f);
Assert(instance->position.z == 5.0f);
// Verify back-pointer invariant preserved after revert
Assert(inertDerived->base == instance);
ShutdownEntityManager();
ShutdownWorld(&world);
LogMessage(LogCategory::Game, "[UnitTest] TestDeltaOverrideAndRevert PASSED.");
}
// =========================================================================
// Master Runner
// =========================================================================
void RunTemplateAndInheritanceUnitTests()
{
TempArena scratch = scratch_begin(nullptr, 0);
TestKvParser(scratch.Arena);
TestTemplateCache(scratch.Arena);
TestInstantiationPipeline(scratch.Arena);
TestDeltaOverrideAndRevert(scratch.Arena);
scratch_end(scratch);
LogMessage(LogCategory::Game, "[UnitTest] All Entity Template & Inheritance tests PASSED successfully.");
}
}
#endif
+273
View File
@@ -0,0 +1,273 @@
# Entity Removal & Deletion Lifecycle (Brainstorm & Rework)
> [!WARNING]
> **Status: Needs Rework**
> The entity deletion and removal logic below was extracted as-is from [`Game/Plans/02_Entity_Allocation_And_Lifecycle.md`](file:///w:/Classified/Juliet/Game/Plans/02_Entity_Allocation_And_Lifecycle.md) for future brainstorming.
> The original swap-and-pop approach required complex mutual back-pointer fixups across both base and derived memory buffers.
---
## 1. Brainstorming Notes & Alternative Approaches
The original design relied on `VectorArena::RemoveAtFast` (swap-and-pop) for both `Entities` and `by_type[kind].arena`. While this maintained dense contiguous memory, swapping arbitrary elements in memory invalidated pointers in both directions, requiring runtime pointer patching (`derived->Base = movedEntity; base->Derived = movedComponent`).
### Ideas for Simpler & More Robust Alternatives:
1. **Active Flag / Tombstone (`is_active` bool)**:
- Keep deleted entities in place rather than moving or swapping them.
- Simply toggle `entity->is_active = false;`.
- Iterators and simulation loops skip inactive entities (`if (!entity->is_active) continue;`).
- Pointers to entities and derived components remain stable for their entire lifetime.
2. **Intrusive Free List for Slot Recycling**:
- Instead of shifting memory on deletion, vacant slots are linked into an intrusive free list (`first_free_index`).
- When an entity is destroyed:
- `entity->is_active = false;`
- Overwrite unused slot memory with `next_free_index`.
- When allocating a new entity:
- Check if `first_free_index != indexMax`.
- Pop from the free list in $O(1)$; otherwise push back to the end of the arena.
- Zero pointer invalidation, zero memmove/memcpy overhead during deletion.
3. **Stable Generational Handles / Slot Map**:
- If external systems need references to entities that might be deleted, combine the free list with a generational counter to detect stale lookups safely.
4. **Deferred Compaction / Garbage Collection**:
- If contiguous packing is strictly necessary for SIMD/cache optimization, defer compaction to a designated level load or scene transition rather than doing it per-frame on individual deletes.
---
## 2. Extracted Original Removal Logic (As-Is from Plan 2)
### 2.1 In-Memory Removal vs. Immediate Disk Deletion Hazards
In game development, deleting an entity in the editor or during gameplay must **never synchronously invoke disk deletion**:
1. **Frame Rate Stutters:** Blocking on synchronous OS filesystem APIs (`DeleteFileA`) introduces multisecond frame freezes.
2. **Transactional Safety:** If the editor crashes or the user exits without saving, disk modifications cannot be rolled back.
3. **Undo/Redo Support:** An editor action stack must allow recovering deleted entities before changes are permanently committed to disk.
Therefore, Juliet enforces a strict separation:
- **Immediate in-memory destruction:** Releases the entity from active simulation and registers its identifier in `World::PendingDeletions`.
- **Deferred disk deletion:** Executed strictly during explicit `SaveWorld` operations.
### 2.2 Fast In-Memory Removal (`RemoveAtFast`) & Mutual Pointer Fixup
`VectorArena::RemoveAtFast` utilizes swap-and-pop: the element at the target index is replaced by the last element in the vector, and `Count` is decremented.
```cpp
void RemoveAtFast(index_t index)
{
Assert(Arena);
Assert(index < Count);
Assert(Count > 0);
Type* elementAdr = DataFirst + index;
if (DataLast != elementAdr)
{
Swap(DataLast, elementAdr);
}
--DataLast;
--Count;
}
```
#### The Pointer Invalidation Problem:
When `Entity A` (at `index`) is swapped with `Entity Z` (at `DataLast`), the physical address of `Entity Z` changes from `DataLast` to `elementAdr`.
If `Entity Z` has a derived struct `derivedZ`, `derivedZ->Base` previously pointed to `DataLast`. After `RemoveAtFast`, `derivedZ->Base` points to garbage or the freed slot!
#### The Pointer Fixup Protocol:
To preserve the mutual back-pointer invariant, `DestroyEntity` explicitly fixes up the swapped entity's derived back-pointer:
```cpp
void DestroyEntity(EntityManager& manager, EntityID id)
{
Entity* baseArray = manager.Entities.DataPtr();
size_t count = manager.Entities.Size();
size_t targetIndex = indexMax;
for (size_t i = 0; i < count; ++i)
{
if (baseArray[i].ID == id)
{
targetIndex = i;
break;
}
}
if (targetIndex == indexMax)
{
return;
}
Entity* targetEntity = &baseArray[targetIndex];
Class* classPtr = targetEntity->derived_kind;
Assert(classPtr != nullptr);
// 1. Remove derived component from typed array via swap-and-pop
RemoveDerivedComponent(manager, classPtr, targetEntity->derived);
// 2. Remove base entity via swap-and-pop in VectorArena
bool wasLast = (targetIndex == count - 1);
manager.Entities.RemoveAtFast(targetIndex);
// 3. Pointer fixup: If an element was swapped into targetIndex, fix its back-pointer!
if (!wasLast && targetIndex < manager.Entities.Size())
{
Entity* movedEntity = &manager.Entities[targetIndex];
auto* derivedTemp = reinterpret_cast<entity_template*>(movedEntity->derived);
Assert(derivedTemp != nullptr);
derivedTemp->base = movedEntity;
}
}
```
### 2.3 O(1) Component Removal in `typed_entity_array` via Swap-and-Pop
To keep derived components packed contiguously for SIMD/cache iteration:
1. Locate the component's index within `by_type[kind].arena`. Because components have uniform stride `classPtr->size_of`:
$$\text{componentIndex} = \frac{\text{reinterpret\_cast<uint8*>(derivedPtr)} - \text{reinterpret\_cast<uint8*>(typedArray.array)}}{\text{classPtr->size\_of}}$$
2. If the component is not the last one in the typed arena:
- Copy the last component into the slot occupied by the deleted component.
- Update the moved component's `Base->Derived` pointer to point to its new slot.
3. Decrement `typedArray.count`.
4. Pop the arena allocation if it was the top of the stack, or decrement count to mark slot reclamation.
```cpp
void RemoveDerivedComponent(EntityManager& manager, Class* classPtr, DerivedType derivedPtr)
{
Assert(classPtr != nullptr);
Assert(derivedPtr != nullptr);
typed_entity_array& typedArray = manager.by_type[classPtr->kind];
Assert(typedArray.count > 0);
Assert(typedArray.array != nullptr);
size_t stride = classPtr->size_of;
auto* targetByte = reinterpret_cast<uint8*>(derivedPtr);
auto* firstByte = reinterpret_cast<uint8*>(typedArray.array);
size_t componentIndex = static_cast<size_t>(targetByte - firstByte) / stride;
Assert(componentIndex < typedArray.count);
size_t lastIndex = typedArray.count - 1;
if (componentIndex != lastIndex)
{
uint8* lastByte = firstByte + (lastIndex * stride);
// Copy last component data into target slot
MemCopy(targetByte, lastByte, stride);
// Fixup the base pointer of the moved component
auto* movedDerived = reinterpret_cast<entity_template*>(targetByte);
Assert(movedDerived->base != nullptr);
movedDerived->base->derived = targetByte;
}
typedArray.count -= 1;
if (typedArray.count == 0)
{
typedArray.array = nullptr;
}
}
```
### 2.4 Tracking Deletions in `World::PendingDeletions`
In `World.h`, the `World` struct is extended with a pending deletions container:
```cpp
struct World
{
Arena* WorldArena = nullptr;
EntityManager* EntityManager = nullptr;
VectorArena<EntityID, 1024> PendingDeletions;
};
```
When an entity is deleted in the world:
```cpp
void RemoveWorldEntity(World& world, EntityID id)
{
Assert(world.EntityManager != nullptr);
// Record pending disk deletion
world.PendingDeletions.PushBack(id);
// Destroy in memory immediately
DestroyEntity(*world.EntityManager, id);
}
```
### 2.5 Disk File Cleanup during `SaveWorld`
During `SaveWorld`, before saving modified entities, the engine iterates over `world.PendingDeletions` and removes their associated `.jasset` files:
```cpp
void ProcessPendingDeletions(World& world, NonNullPtr<Arena> scratchArena)
{
for (size_t i = 0; i < world.PendingDeletions.Size(); ++i)
{
EntityID id = world.PendingDeletions[i];
// Format relative asset path: Assets/Entities/{ID}.jasset
char filenameBuffer[64];
juliet_snprintf(filenameBuffer, sizeof(filenameBuffer), "Entities/%llu.jasset", id);
String assetPath = GetAssetPath(scratchArena, WrapString(filenameBuffer));
if (PlatformDeleteFile(assetPath))
{
Log(LogLevel::Message, LogCategory::Game, "Deleted entity asset: %s", CStr(assetPath));
}
}
world.PendingDeletions.Clear();
}
```
### 2.6 Associated Swap-and-Pop Unit Test
```cpp
void TestSwapAndPopPointerFixup()
{
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] Running TestSwapAndPopPointerFixup...");
TempArena tempArena = scratch_begin(nullptr, 0);
World testWorld{};
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager& manager = *testWorld.EntityManager;
// Allocate 3 entities: E0, E1, E2
Inert* e0 = MakeEntity<Inert>(manager, 1.0f, 0.0f, 0.0f);
Inert* e1 = MakeEntity<Inert>(manager, 2.0f, 0.0f, 0.0f);
Inert* e2 = MakeEntity<Inert>(manager, 3.0f, 0.0f, 0.0f);
EntityID id0 = e0->base->ID;
EntityID id1 = e1->base->ID;
EntityID id2 = e2->base->ID;
Assert(manager.Entities.Size() == 3);
// Delete middle entity E1 (triggers swap with E2)
DestroyEntity(manager, id1);
Assert(manager.Entities.Size() == 2);
// Verify E2's mutual back-pointers are still completely intact
Entity* remaining0 = &manager.Entities[0];
Entity* remaining1 = &manager.Entities[1];
Assert(remaining0->ID == id0);
Assert(remaining1->ID == id2);
auto* derived0 = reinterpret_cast<entity_template*>(remaining0->derived);
auto* derived1 = reinterpret_cast<entity_template*>(remaining1->derived);
Assert(derived0->base == remaining0);
Assert(derived1->base == remaining1);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
scratch_end(tempArena);
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] PASSED: TestSwapAndPopPointerFixup");
}
```
+171
View File
@@ -0,0 +1,171 @@
#include <UnitTest/WorldUnitTest.h>
#if JULIET_DEBUG
#include <Core/Common/CoreUtils.h>
#include <Core/Common/serialization.h>
#include <Core/HAL/IO/IOStream.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Thread/ThreadContext.h>
#include <Data/World.h>
#include <Entity/Entity.h>
#include <Entity/entity_types.h>
#include <game.h>
internal void TestEntityAllocationAndWiring(void)
{
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] Running TestEntityAllocationAndWiring...");
TempArena tempArena = scratch_begin(NULL, 0);
GameState* gameState = GetGameState();
World testWorld = { 0 };
gameState->World = &testWorld;
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager* manager = testWorld.EntityManager;
// 1. Allocate Inert Entity via allocate_entity
Entity* base_entity = allocate_entity(manager, ClassKind(Inert));
Assert(base_entity != NULL);
Assert(base_entity->ID == 0); // Pure memory allocator leaves ID unassigned (0) until MakeEntity or deserialization
base_entity->ID = ++g_entity_manager_id;
Assert(base_entity->ID > 0);
Assert(base_entity->derived_kind == ClassKind(Inert));
Assert(base_entity->derived != NULL);
Assert(base_entity->is_dirty == true);
// 2. Validate mutual back-pointer wiring
EntityTemplate* derived = (EntityTemplate*)base_entity->derived;
Assert(derived->base == base_entity);
// 3. DownCast verification
Inert* inert = DownCastEntity(Inert, base_entity);
Assert(inert != NULL);
Assert(inert->base == base_entity);
// 4. Validate typed array tracking
typed_entity_array* inert_array = &manager->by_type[ENTITY(Inert)];
Assert(inert_array->count == 1);
Assert(inert_array->array == derived);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
gameState->World = NULL;
scratch_end(tempArena);
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] PASSED: TestEntityAllocationAndWiring");
}
internal void TestInPlaceDeserialization(void)
{
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] Running TestInPlaceDeserialization...");
TempArena tempArena = scratch_begin(NULL, 0);
GameState* gameState = GetGameState();
World testWorld = { 0 };
gameState->World = &testWorld;
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager* manager = testWorld.EntityManager;
// 1. In-memory .jasset text fixture (no streams or files needed)
String asset_content = ConstString("; Class\n"
"Inert\n"
"; version\n"
"1\n"
"; class_version\n"
"1\n"
"; id\n"
"42\n"
"; position\n"
"12.5 -44.0 108.2 1.0\n"
"; MeshInstance\n"
"42\n");
ByteBuffer buffer = { .Data = (Byte*)asset_content.Str, .Size = asset_content.Size };
// 2. Tokenize into Archive
Archive load_ar = { 0 };
load_ar.arena = tempArena.Arena;
load_ar.loading = true;
load_ar.base = tokenize_archive(tempArena.Arena, buffer);
// 3. Deserialize in-place
Entity* loaded_base = deserialize_entity(&load_ar, manager);
// 4. Verify in-place allocations and values
Assert(loaded_base != NULL);
Assert(loaded_base->ID == 42);
Assert(g_entity_manager_id > 42); // Generator counter advanced past loaded ID
Assert(loaded_base->position.x == 12.5f);
Assert(loaded_base->position.y == -44.0f);
Assert(loaded_base->position.z == 108.2f);
Assert(loaded_base->is_dirty == false);
Inert* loaded_inert = DownCastEntity(Inert, loaded_base);
Assert(loaded_inert != NULL);
Assert(loaded_inert->base == loaded_base);
Assert(loaded_inert->MeshInstance == 42);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
gameState->World = NULL;
scratch_end(tempArena);
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] PASSED: TestInPlaceDeserialization");
}
internal void TestDirtyTrackingLifecycle(void)
{
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] Running TestDirtyTrackingLifecycle...");
TempArena tempArena = scratch_begin(NULL, 0);
GameState* gameState = GetGameState();
World testWorld = { 0 };
gameState->World = &testWorld;
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager* manager = testWorld.EntityManager;
Entity* entity = allocate_entity(manager, ClassKind(Inert));
Assert(entity->is_dirty == true);
// TODO : proper save and mutation
// Simulate save
entity->is_dirty = false;
Assert(entity->is_dirty == false);
// Simulate mutation
entity->position.x += 1.0f;
entity->is_dirty = true;
Assert(entity->is_dirty == true);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
gameState->World = NULL;
scratch_end(tempArena);
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] PASSED: TestDirtyTrackingLifecycle");
}
void UnitTest_WorldUnitTest(void)
{
Log(LogLevel_Message, LogCategory_Game, "==================================================");
Log(LogLevel_Message, LogCategory_Game, "Starting Entity Allocation & Lifecycle Unit Tests");
Log(LogLevel_Message, LogCategory_Game, "==================================================");
TestEntityAllocationAndWiring();
TestInPlaceDeserialization();
// TestSwapAndPopPointerFixup(); // Deferred to Entity_Removal_Brainstorm.md
TestDirtyTrackingLifecycle();
Log(LogLevel_Message, LogCategory_Game, "==================================================");
Log(LogLevel_Message, LogCategory_Game, "All Entity Lifecycle Unit Tests PASSED Successfully");
Log(LogLevel_Message, LogCategory_Game, "==================================================");
}
#endif
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <Juliet.h>
#if JULIET_DEBUG
void UnitTest_WorldUnitTest(void);
#endif
+167
View File
@@ -0,0 +1,167 @@
#include <game.h>
#include <Controller/DebugCameraController.h>
#include <Core/Common/serialization.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/HAL/Keyboard/Keyboard.h>
#include <Core/JulietInit.h>
#include <Core/Logging/LogManager.h>
#include <Core/Memory/MemoryArena.h>
#include <Data/World.h>
#include <Entity/Entity.h>
#include <Entity/entity_types.h>
#include <Entity/EntityManager.h>
#include <Graphics/Camera.h>
#include <Graphics/MeshRenderer.h>
#include <dcimgui.h>
#include <stdio.h>
#if JULIET_DEBUG
#include <Debug/DebugTopBar.h>
#include <UnitTest/WorldUnitTest.h>
#endif
global GameState* gGameState = NULL;
GameState* GetGameState(void)
{
return gGameState;
}
JULIET_API void __cdecl GameShutdown(void)
{
printf("Shutting down game...\n");
if (gGameState != NULL && gGameState->World != NULL)
{
ShutdownWorld(gGameState->World);
}
ShutdownEntityManager();
}
JULIET_API void __cdecl GameUpdate(GameData* params, float deltaTime)
{
Assert(params != NULL);
gGameState = params->GameState;
if (gGameState == NULL)
{
ArenaParams gameStateParams = { .ReserveSize = Megabytes(1), .Name = "Game Total Arena" };
Arena* gameStateArena = ArenaAllocate(gameStateParams);
GameState* gameState = ArenaPushStruct(gameStateArena, GameState);
gGameState = params->GameState = gameState;
gameState->TotalArena = gameStateArena;
gameState->Mode = GameMode_Editor;
gameState->TotalTime = 0.0f;
gameState->Score = 0;
printf("Game Arena Allocated: %p\n", (void*)gameState);
// Reserve cameras. Lets go with 4 for now
ReserveCamera(4);
#if JULIET_DEBUG
UnitTest_WorldUnitTest();
#endif
// Bootstrap world
ArenaParams worldParams = { .ReserveSize = Megabytes(1), .Name = "World Arena" };
Arena* worldArena = ArenaAllocate(worldParams);
World* world = ArenaPushStructTag(worldArena, World, "World");
gameState->World = world;
InitWorld(gameState->World, worldArena);
// Entity Use case
InitEntityManager(gameState->World);
EntityManager* manager = GetEntityManager();
Inert* mesh = MakeEntity(Inert, manager, 1.f, 2.f, 0.f);
Assert(mesh != NULL);
MeshAssetID cubeAsset = GetCubePrimitiveMeshAssetID();
Matrix identity = MatrixIdentity();
MeshInstanceID meshInst = CreateMeshInstance(cubeAsset, 0, &identity);
mesh->MeshInstance = meshInst;
Inert* mesh2 = MakeEntity(Inert, manager, 4.f, 0.f, 2.f);
Assert(mesh2 != NULL);
MeshInstanceID meshInst2 = CreateMeshInstance(cubeAsset, 0, &identity);
mesh2->MeshInstance = meshInst2;
// Summer at 2pm lighting
Vector3 sunDirection = { -0.2f, -0.9f, -0.3f };
Vector3 sunColor = { 1.0f, 0.95f, 0.85f };
float ambientIntensity = 0.4f;
SetGlobalLight(sunDirection, sunColor, ambientIntensity);
}
#if JULIET_DEBUG
DrawTopBar();
#endif
GameMode previousFrameMode = gGameState->Mode;
if (IsKeyPressed(ScanCode_F1))
{
if (previousFrameMode == GameMode_Play)
{
gGameState->Mode = GameMode_Debug;
}
else if (previousFrameMode == GameMode_Debug)
{
gGameState->Mode = GameMode_Play;
}
}
if (gGameState->Mode == GameMode_Editor)
{
if (IsKeyPressed(ScanCode_F5))
{
gGameState->Mode = GameMode_Play;
}
#if JULIET_EDITOR
RenderWorldEditorUI(gGameState->World);
#endif
}
if (gGameState->Mode == GameMode_Play)
{
if (IsKeyPressed(ScanCode_Escape))
{
gGameState->Mode = GameMode_Editor;
}
// update game
}
EntityManager* manager = GetEntityManager();
UpdateEntityManager(manager);
if (gGameState->Mode == GameMode_Debug)
{
if (previousFrameMode == GameMode_Play)
{
ActivateDebugController();
}
if (IsKeyPressed(ScanCode_Escape))
{
gGameState->Mode = GameMode_Editor;
}
UpdateDebugController(deltaTime);
#if JULIET_DEBUG
ImGui_Begin("Debug", NULL, ImGuiWindowFlags_None);
RenderImGuiDebugController(deltaTime);
ImGui_End();
#endif
}
if (gGameState->Mode != GameMode_Debug && previousFrameMode == GameMode_Debug)
{
DeactivateDebugController();
}
}
-77
View File
@@ -1,77 +0,0 @@
#include <cstdio>
#include <Windows.h> // TODO: remove because our dll should not be platform dependant
#undef min
#undef max
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/JulietInit.h>
#include <Core/Logging/LogManager.h>
#include <Core/Memory/MemoryArena.h>
#include <Entity/Entity.h>
#include <Entity/EntityManager.h>
// Test code
namespace Game
{
struct Door
{
DECLARE_ENTITY()
bool IsOpened;
};
DEFINE_ENTITY(Door);
struct Rock
{
DECLARE_ENTITY()
int Health;
};
DEFINE_ENTITY(Rock);
} // namespace Game
using namespace Juliet;
extern "C" JULIET_API void GameInit(GameInitParams* /*params*/)
{
// Example allocation in GameArena
struct GameState
{
float TotalTime;
int Score;
};
auto* gameState = ArenaPushType<GameState>(GetGameArena());
gameState->TotalTime = 0.0f;
gameState->Score = 0;
printf("Game Arena Allocated: %p\n", static_cast<void*>(gameState));
using namespace Game;
// Entity Use case
InitEntityManager();
auto& manager = GetEntityManager();
Door* door = MakeEntity<Door>(manager, 10.0f, 2.0f);
door->IsOpened = true;
Entity* ent = door->Base;
[[maybe_unused]] Door* stillDoor = DownCast<Door>(ent);
Assert(door == stillDoor);
Rock* rock = MakeEntity<Rock>(manager, 1.f, 2.f);
rock->Health = 100;
Assert(door->Base != rock->Base);
printf("Door is %s\n", door->IsOpened ? "Opened" : "Closed");
printf("Rock has %d health points\n", rock->Health);
}
extern "C" JULIET_API void __cdecl GameShutdown()
{
printf("Shutting down game...\n");
}
extern "C" JULIET_API void __cdecl GameUpdate([[maybe_unused]] float deltaTime)
{
// printf("Updating game...\n");
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include <Core/Memory/MemoryArena.h>
#include <Data/World.h>
typedef struct EntityManager EntityManager;
typedef uint32 GameMode;
enum
{
GameMode_Editor = 0,
GameMode_Play,
GameMode_Debug
};
typedef struct GameState GameState;
struct GameState
{
Arena* TotalArena;
World* World;
GameMode Mode;
float TotalTime;
int Score;
};
extern GameState* GetGameState(void);
+8
View File
@@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}
-109
View File
@@ -1,109 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22823.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Game", "Game\Game.vcxproj", "{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JulietApp", "JulietApp\JulietApp.vcxproj", "{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Juliet", "Juliet\Juliet.vcxproj", "{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x64 = Debug|x64
Debug|x64 = Debug|x64
Profile|x64 = Profile|x64
Profile|x64 = Profile|x64
Profile|x64 = Profile|x64
Release|x64 = Release|x64
Release|x64 = Release|x64
Release|x64 = Release|x64
Debug|x64Clang = Debug|x64Clang
Debug|x64Clang = Debug|x64Clang
Debug|x64Clang = Debug|x64Clang
Profile|x64Clang = Profile|x64Clang
Profile|x64Clang = Profile|x64Clang
Profile|x64Clang = Profile|x64Clang
Release|x64Clang = Release|x64Clang
Release|x64Clang = Release|x64Clang
Release|x64Clang = Release|x64Clang
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.ActiveCfg = x64-Debug|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.ActiveCfg = x64-Debug|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.ActiveCfg = x64-Debug|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.ActiveCfg = x64-Profile|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.ActiveCfg = x64-Profile|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.ActiveCfg = x64-Profile|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.ActiveCfg = x64-Release|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.ActiveCfg = x64-Release|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.ActiveCfg = x64-Release|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.ActiveCfg = x64Clang-Debug|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.ActiveCfg = x64Clang-Debug|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.ActiveCfg = x64Clang-Debug|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.ActiveCfg = x64Clang-Profile|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.ActiveCfg = x64Clang-Profile|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.ActiveCfg = x64Clang-Profile|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.ActiveCfg = x64Clang-Release|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.ActiveCfg = x64Clang-Release|x64
{B1D040D0-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.ActiveCfg = x64Clang-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.ActiveCfg = x64-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.Build.0 = x64-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.ActiveCfg = x64-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.Build.0 = x64-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.ActiveCfg = x64-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.Build.0 = x64-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.ActiveCfg = x64-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.Build.0 = x64-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.ActiveCfg = x64-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.Build.0 = x64-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.ActiveCfg = x64-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.Build.0 = x64-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.ActiveCfg = x64-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.Build.0 = x64-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.ActiveCfg = x64-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.Build.0 = x64-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.ActiveCfg = x64-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.Build.0 = x64-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.ActiveCfg = x64Clang-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.Build.0 = x64Clang-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.ActiveCfg = x64Clang-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.Build.0 = x64Clang-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.ActiveCfg = x64Clang-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.Build.0 = x64Clang-Debug|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.ActiveCfg = x64Clang-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.Build.0 = x64Clang-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.ActiveCfg = x64Clang-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.Build.0 = x64Clang-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.ActiveCfg = x64Clang-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.Build.0 = x64Clang-Profile|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.ActiveCfg = x64Clang-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.Build.0 = x64Clang-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.ActiveCfg = x64Clang-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.Build.0 = x64Clang-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.ActiveCfg = x64Clang-Release|x64
{1DEE51CA-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.Build.0 = x64Clang-Release|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.ActiveCfg = x64-Debug|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.ActiveCfg = x64-Debug|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64.ActiveCfg = x64-Debug|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.ActiveCfg = x64-Profile|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.ActiveCfg = x64-Profile|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64.ActiveCfg = x64-Profile|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.ActiveCfg = x64-Release|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.ActiveCfg = x64-Release|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Release|x64.ActiveCfg = x64-Release|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.ActiveCfg = x64Clang-Debug|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.ActiveCfg = x64Clang-Debug|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Debug|x64Clang.ActiveCfg = x64Clang-Debug|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.ActiveCfg = x64Clang-Profile|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.ActiveCfg = x64Clang-Profile|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Profile|x64Clang.ActiveCfg = x64Clang-Profile|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.ActiveCfg = x64Clang-Release|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.ActiveCfg = x64Clang-Release|x64
{AB9C7E88-6C94-4F93-BC2A-7F5284B7D434}.Release|x64Clang.ActiveCfg = x64Clang-Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
-79
View File
@@ -1,79 +0,0 @@
// Juliet - Lib/Dll
//------------------------------------------------------------------------------
{
.ProjectName = 'Juliet'
.ProjectPath = 'Juliet'
.JulietIncludePath = ' "-IJuliet/include"'
+ ' "-IJuliet/src"'
// Library
//--------------------------------------------------------------------------
ForEach( .BuildConfig in .BuildConfigs )
{
Using( .BuildConfig )
.OutputBase + '\$Platform$-$BuildConfigName$'
// Unity
//--------------------------------------------------------------------------
Unity( '$ProjectName$-Unity-$Platform$-$BuildConfigName$' )
{
.UnityInputPath = '$ProjectPath$/'
.UnityOutputPath = '$OutputBase$/$ProjectPath$/'
.UnityOutputPattern = '$ProjectName$_Unity*.cpp'
}
// Library
//--------------------------------------------------------------------------
ObjectList( '$ProjectName$-Objs-$Platform$-$BuildConfigName$' )
{
// Input (Unity)
.CompilerInputUnity = '$ProjectName$-Unity-$Platform$-$BuildConfigName$'
// Extra Compiler Options
.CompilerOptions + .JulietIncludePath
.CompilerOptions + ' -DJULIET_EXPORT'
#if __WINDOWS__
.CompilerOptions + ' -DJULIET_WIN32'
#endif
// Output
.CompilerOutputPath = '$OutputBase$/$ProjectPath$/'
}
// --- DLL BUILD ---
DLL( '$ProjectName$-Lib-$Platform$-$BuildConfigName$' )
{
.Libraries = { '$ProjectName$-Objs-$Platform$-$BuildConfigName$' }
.LinkerOutput = '$BinPath$/$Platform$-$BuildConfigName$/$ProjectName$.dll' // Output .dll to Bin
#if __WINDOWS__
.LinkerOptions + ' /DLL'
.LinkerOptions + .CommonWinLibs
.CRTLibs = .CRTLibs_Dynamic
If ( .BuildConfigName == 'Debug' )
{
^CRTLibs = .CRTLibs_DynamicDebug
}
.LinkerOptions + .CRTLibs
#endif
}
Alias( '$ProjectName$-$Platform$-$BuildConfigName$' ) { .Targets = '$ProjectName$-Lib-$Platform$-$BuildConfigName$' }
^'Targets_$Platform$_$BuildConfigName$' + { '$ProjectName$-$Platform$-$BuildConfigName$' }
#if __WINDOWS__
.ProjectConfig = [ Using( .'Project_$Platform$_$BuildConfigName$' ) .Target = '$ProjectName$-$Platform$-$BuildConfigName$' ]
^ProjectConfigs + .ProjectConfig
#endif
}
VCXProject( '$ProjectName$' )
{
.ProjectOutput = '$ProjectPath$/$ProjectName$.vcxproj'
.ProjectBasePath = '$ProjectPath$/'
.ProjectInputPaths = .ProjectBasePath
.ProjectConfigs = .ProjectConfigs
}
}
-345
View File
@@ -1,345 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="x64-Debug|x64">
<Configuration>x64-Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64-Profile|x64">
<Configuration>x64-Profile</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64-Release|x64">
<Configuration>x64-Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64Clang-Debug|x64">
<Configuration>x64Clang-Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64Clang-Profile|x64">
<Configuration>x64Clang-Profile</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="x64Clang-Release|x64">
<Configuration>x64Clang-Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="include\Core\Application\ApplicationManager.h" />
<CustomBuild Include="include\Core\Application\IApplication.h" />
<CustomBuild Include="include\Core\Common\CoreTypes.h" />
<CustomBuild Include="include\Core\Common\CoreUtils.h" />
<CustomBuild Include="include\Core\Common\CRC32.h" />
<CustomBuild Include="include\Core\Common\EnumUtils.h" />
<CustomBuild Include="include\Core\Common\NonCopyable.h" />
<CustomBuild Include="include\Core\Common\NonMovable.h" />
<CustomBuild Include="include\Core\Common\NonNullPtr.h" />
<CustomBuild Include="include\Core\Common\Singleton.h" />
<CustomBuild Include="include\Core\Common\String.h" />
<CustomBuild Include="include\Core\Container\Vector.h" />
<CustomBuild Include="include\Core\HAL\Display\Display.h" />
<CustomBuild Include="include\Core\HAL\DynLib\DynamicLibrary.h" />
<CustomBuild Include="include\Core\HAL\Event\SystemEvent.h" />
<CustomBuild Include="include\Core\HAL\Filesystem\Filesystem.h" />
<CustomBuild Include="include\Core\HAL\IO\IOStream.h" />
<CustomBuild Include="include\Core\HAL\Keyboard\Keyboard.h" />
<CustomBuild Include="include\Core\HAL\Keyboard\KeyCode.h" />
<CustomBuild Include="include\Core\HAL\Keyboard\ScanCode.h" />
<CustomBuild Include="include\Core\HAL\Mouse\Mouse.h" />
<CustomBuild Include="include\Core\HotReload\HotReload.h" />
<CustomBuild Include="include\Core\JulietInit.h" />
<CustomBuild Include="include\Core\Logging\LogManager.h" />
<CustomBuild Include="include\Core\Logging\LogTypes.h" />
<CustomBuild Include="include\Core\Math\MathUtils.h" />
<CustomBuild Include="include\Core\Math\Matrix.h" />
<CustomBuild Include="include\Core\Math\Shape.h" />
<CustomBuild Include="include\Core\Math\Vector.h" />
<CustomBuild Include="include\Core\Memory\Allocator.h" />
<CustomBuild Include="include\Core\Memory\MemoryArena.h" />
<CustomBuild Include="include\Core\Memory\ScratchArena.h" />
<CustomBuild Include="include\Core\Memory\Utils.h" />
<CustomBuild Include="include\Core\Networking\IPAddress.h" />
<CustomBuild Include="include\Core\Networking\NetworkPacket.h" />
<CustomBuild Include="include\Core\Networking\Socket.h" />
<CustomBuild Include="include\Core\Networking\SocketHandle.h" />
<CustomBuild Include="include\Core\Networking\TcpListener.h" />
<CustomBuild Include="include\Core\Networking\TcpSocket.h" />
<CustomBuild Include="include\Core\Thread\Mutex.h" />
<CustomBuild Include="include\Core\Thread\Thread.h" />
<CustomBuild Include="include\Engine\Class.h" />
<CustomBuild Include="include\Engine\Engine.h" />
<CustomBuild Include="include\Graphics\Camera.h" />
<CustomBuild Include="include\Graphics\Colors.h" />
<CustomBuild Include="include\Graphics\DebugDisplay.h" />
<CustomBuild Include="include\Graphics\Graphics.h" />
<CustomBuild Include="include\Graphics\GraphicsBuffer.h" />
<CustomBuild Include="include\Graphics\GraphicsConfig.h" />
<CustomBuild Include="include\Graphics\GraphicsPipeline.h" />
<CustomBuild Include="include\Graphics\RenderPass.h" />
<CustomBuild Include="include\Graphics\Shader.h" />
<CustomBuild Include="include\Graphics\Texture.h" />
<CustomBuild Include="include\Juliet.h" />
<CustomBuild Include="Juliet.bff" />
<CustomBuild Include="src\Core\Application\ApplicationManager.cpp" />
<CustomBuild Include="src\Core\Common\CoreUtils.cpp" />
<CustomBuild Include="src\Core\Common\String.cpp" />
<CustomBuild Include="src\Core\HAL\Display\Display.cpp" />
<CustomBuild Include="src\Core\HAL\Display\Display_Private.h" />
<CustomBuild Include="src\Core\HAL\Display\DisplayDevice.h" />
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32DisplayDevice.cpp" />
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32DisplayEvent.cpp" />
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32DisplayEvent.h" />
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32Window.cpp" />
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32Window.h" />
<CustomBuild Include="src\Core\HAL\Display\Window.h" />
<CustomBuild Include="src\Core\HAL\DynLib\Win32\DynamicLibrary.cpp" />
<CustomBuild Include="src\Core\HAL\Event\Keyboard.cpp" />
<CustomBuild Include="src\Core\HAL\Event\Keyboard_Private.h" />
<CustomBuild Include="src\Core\HAL\Event\KeyboardMapping.cpp" />
<CustomBuild Include="src\Core\HAL\Event\KeyboardMapping.h" />
<CustomBuild Include="src\Core\HAL\Event\Mouse.cpp" />
<CustomBuild Include="src\Core\HAL\Event\Mouse_Private.h" />
<CustomBuild Include="src\Core\HAL\Event\SystemEvent.cpp" />
<CustomBuild Include="src\Core\HAL\Event\Win32ScanCode.h" />
<CustomBuild Include="src\Core\HAL\Event\WindowEvent.cpp" />
<CustomBuild Include="src\Core\HAL\Event\WindowEvent.h" />
<CustomBuild Include="src\Core\HAL\Filesystem\Filesystem.cpp" />
<CustomBuild Include="src\Core\HAL\Filesystem\Filesystem_Platform.h" />
<CustomBuild Include="src\Core\HAL\Filesystem\Filesystem_Private.h" />
<CustomBuild Include="src\Core\HAL\Filesystem\Win32\Win32Filesystem.cpp" />
<CustomBuild Include="src\Core\HAL\IO\IOStream.cpp" />
<CustomBuild Include="src\Core\HAL\IO\IOStream_Private.h" />
<CustomBuild Include="src\Core\HAL\IO\Win32\Win32IOStream.cpp" />
<CustomBuild Include="src\Core\HAL\Win32.h" />
<CustomBuild Include="src\Core\HotReload\HotReload.cpp" />
<CustomBuild Include="src\Core\HotReload\Win32\Win32HotReload.cpp" />
<CustomBuild Include="src\Core\Juliet.cpp" />
<CustomBuild Include="src\Core\Logging\LogManager.cpp" />
<CustomBuild Include="src\Core\Math\Math_Private.h" />
<CustomBuild Include="src\Core\Math\MathRound.cpp" />
<CustomBuild Include="src\Core\Memory\Allocator.cpp" />
<CustomBuild Include="src\Core\Memory\EngineArena.h" />
<CustomBuild Include="src\Core\Memory\MemoryArena.cpp" />
<CustomBuild Include="src\Core\Memory\MemoryArenaTests.cpp" />
<CustomBuild Include="src\Core\Memory\ScratchArena.cpp" />
<CustomBuild Include="src\Core\Networking\NetworkPacket.cpp" />
<CustomBuild Include="src\Core\Networking\Socket.cpp" />
<CustomBuild Include="src\Core\Networking\SocketPlatformImpl.h" />
<CustomBuild Include="src\Core\Networking\TcpListener.cpp" />
<CustomBuild Include="src\Core\Networking\TcpSocket.cpp" />
<CustomBuild Include="src\Core\Networking\Win32\Win32SocketPlatformImpl.cpp" />
<CustomBuild Include="src\Engine\Engine.cpp" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12compatibility.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12sdklayers.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12shader.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\D3D12TokenizedProgramFormat.hpp" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12video.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dcommon.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_barriers.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_check_feature_support.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_core.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_default.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_pipeline_state_stream.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_property_format_table.cpp" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_property_format_table.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_render_pass.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_resource_helpers.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_root_signature.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_state_object.h" />
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\dxgiformat.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Buffer.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Buffer.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12CommandList.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12CommandList.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Common.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Common.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12DescriptorHeap.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12DescriptorHeap.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12GraphicsDevice.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12GraphicsDevice.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12GraphicsPipeline.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12GraphicsPipeline.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Includes.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12InternalTests.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12InternalTests.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12RenderPass.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12RenderPass.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Shader.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Shader.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12SwapChain.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12SwapChain.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Synchronization.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Synchronization.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Texture.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Texture.h" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Utils.cpp" />
<CustomBuild Include="src\Graphics\D3D12\D3D12Utils.h" />
<CustomBuild Include="src\Graphics\DebugDisplayRenderer.cpp" />
<CustomBuild Include="src\Graphics\Graphics.cpp" />
<CustomBuild Include="src\Graphics\GraphicsDevice.h" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{ab9c7e88-6c94-4f93-bc2a-7f5284b7d434}</ProjectGuid>
<Keyword>MakeFileProj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<LocalDebuggerCommand>$(SolutionDir)\bin\$(Configuration)\$(ProjectName).exe</LocalDebuggerCommand>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;DEBUG;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;include;src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;include;src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;include;src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;DEBUG;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;include;src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;PROFILING_ENABLED;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;include;src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'">
<NMakeBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache $(ProjectName)-$(Configuration)</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>cd $(SolutionDir) &amp; misc\fbuild -ide -dist -monitor -cache -clean $(ProjectName)-$(Configuration)</NMakeReBuildCommandLine>
<NMakePreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32_LEAN_AND_MEAN;WIN32;_WIN32;__WINDOWS__;_HAS_EXCEPTIONS=0;WIN64;RELEASE;JULIET_EXPORT;JULIET_WIN32;</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>..\;include;src;C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared;</NMakeIncludeSearchPath>
<AdditionalOptions>/std:c++20 /wd5267 /wd4061 /wd4505 /wd4514 /wd4577 /wd4625 /wd4710 /wd4711 /wd4746 /wd4820 /wd5045 /wd5220 /wd5245 </AdditionalOptions>
<LocalDebuggerWorkingDirectory>$(SolutionDir)\bin\$(Configuration)\</LocalDebuggerWorkingDirectory>
<IntDir>$(SolutionDir)\Intermediate</IntDir>
<OutDir>$(SolutionDir)\bin</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64-Debug|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64-Profile|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64-Release|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Debug|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Profile|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='x64Clang-Release|x64'">
<BuildLog>
<Path>$(SolutionDir)\Intermediate\$(ProjectName)-$(Configuration).log</Path>
</BuildLog>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<Import Condition="'$(ConfigurationType)' == 'Makefile' and Exists('$(VCTargetsPath)\Platforms\$(Platform)\SCE.Makefile.$(Platform).targets')" Project="$(VCTargetsPath)\Platforms\$(Platform)\SCE.Makefile.$(Platform).targets" />
<Import Condition="'$(ConfigurationType)' == 'Makefile' and '$(AndroidAPILevel)' != '' and Exists('$(VCTargetsPath)\Application Type\$(ApplicationType)\$(ApplicationTypeRevision)\Android.Common.targets')" Project="$(VCTargetsPath)\Application Type\$(ApplicationType)\$(ApplicationTypeRevision)\Android.Common.targets" />
</Project>
-701
View File
@@ -1,701 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<CustomBuild Include="include\Core\Application\ApplicationManager.h">
<Filter>include\Core\Application</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Application\IApplication.h">
<Filter>include\Core\Application</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Common\CoreTypes.h">
<Filter>include\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Common\CoreUtils.h">
<Filter>include\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Common\CRC32.h">
<Filter>include\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Common\EnumUtils.h">
<Filter>include\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Common\NonCopyable.h">
<Filter>include\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Common\NonMovable.h">
<Filter>include\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Common\NonNullPtr.h">
<Filter>include\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Common\Singleton.h">
<Filter>include\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Common\String.h">
<Filter>include\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Container\Vector.h">
<Filter>include\Core\Container</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HAL\Display\Display.h">
<Filter>include\Core\HAL\Display</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HAL\DynLib\DynamicLibrary.h">
<Filter>include\Core\HAL\DynLib</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HAL\Event\SystemEvent.h">
<Filter>include\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HAL\Filesystem\Filesystem.h">
<Filter>include\Core\HAL\Filesystem</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HAL\IO\IOStream.h">
<Filter>include\Core\HAL\IO</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HAL\Keyboard\Keyboard.h">
<Filter>include\Core\HAL\Keyboard</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HAL\Keyboard\KeyCode.h">
<Filter>include\Core\HAL\Keyboard</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HAL\Keyboard\ScanCode.h">
<Filter>include\Core\HAL\Keyboard</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HAL\Mouse\Mouse.h">
<Filter>include\Core\HAL\Mouse</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\HotReload\HotReload.h">
<Filter>include\Core\HotReload</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\JulietInit.h">
<Filter>include\Core</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Logging\LogManager.h">
<Filter>include\Core\Logging</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Logging\LogTypes.h">
<Filter>include\Core\Logging</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Math\MathUtils.h">
<Filter>include\Core\Math</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Math\Matrix.h">
<Filter>include\Core\Math</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Math\Shape.h">
<Filter>include\Core\Math</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Math\Vector.h">
<Filter>include\Core\Math</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Memory\Allocator.h">
<Filter>include\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Memory\MemoryArena.h">
<Filter>include\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Memory\ScratchArena.h">
<Filter>include\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Memory\Utils.h">
<Filter>include\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Networking\IPAddress.h">
<Filter>include\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Networking\NetworkPacket.h">
<Filter>include\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Networking\Socket.h">
<Filter>include\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Networking\SocketHandle.h">
<Filter>include\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Networking\TcpListener.h">
<Filter>include\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Networking\TcpSocket.h">
<Filter>include\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Thread\Mutex.h">
<Filter>include\Core\Thread</Filter>
</CustomBuild>
<CustomBuild Include="include\Core\Thread\Thread.h">
<Filter>include\Core\Thread</Filter>
</CustomBuild>
<CustomBuild Include="include\Engine\Class.h">
<Filter>include\Engine</Filter>
</CustomBuild>
<CustomBuild Include="include\Engine\Engine.h">
<Filter>include\Engine</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\Camera.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\Colors.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\DebugDisplay.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\Graphics.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\GraphicsBuffer.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\GraphicsConfig.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\GraphicsPipeline.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\RenderPass.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\Shader.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Graphics\Texture.h">
<Filter>include\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="include\Juliet.h">
<Filter>include</Filter>
</CustomBuild>
<CustomBuild Include="Juliet.bff">
</CustomBuild>
<CustomBuild Include="src\Core\Application\ApplicationManager.cpp">
<Filter>src\Core\Application</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Common\CoreUtils.cpp">
<Filter>src\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Common\String.cpp">
<Filter>src\Core\Common</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Display\Display.cpp">
<Filter>src\Core\HAL\Display</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Display\Display_Private.h">
<Filter>src\Core\HAL\Display</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Display\DisplayDevice.h">
<Filter>src\Core\HAL\Display</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32DisplayDevice.cpp">
<Filter>src\Core\HAL\Display\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32DisplayEvent.cpp">
<Filter>src\Core\HAL\Display\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32DisplayEvent.h">
<Filter>src\Core\HAL\Display\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32Window.cpp">
<Filter>src\Core\HAL\Display\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Display\Win32\Win32Window.h">
<Filter>src\Core\HAL\Display\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Display\Window.h">
<Filter>src\Core\HAL\Display</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\DynLib\Win32\DynamicLibrary.cpp">
<Filter>src\Core\HAL\DynLib\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\Keyboard.cpp">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\Keyboard_Private.h">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\KeyboardMapping.cpp">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\KeyboardMapping.h">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\Mouse.cpp">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\Mouse_Private.h">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\SystemEvent.cpp">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\Win32ScanCode.h">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\WindowEvent.cpp">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Event\WindowEvent.h">
<Filter>src\Core\HAL\Event</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Filesystem\Filesystem.cpp">
<Filter>src\Core\HAL\Filesystem</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Filesystem\Filesystem_Platform.h">
<Filter>src\Core\HAL\Filesystem</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Filesystem\Filesystem_Private.h">
<Filter>src\Core\HAL\Filesystem</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Filesystem\Win32\Win32Filesystem.cpp">
<Filter>src\Core\HAL\Filesystem\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\IO\IOStream.cpp">
<Filter>src\Core\HAL\IO</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\IO\IOStream_Private.h">
<Filter>src\Core\HAL\IO</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\IO\Win32\Win32IOStream.cpp">
<Filter>src\Core\HAL\IO\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HAL\Win32.h">
<Filter>src\Core\HAL</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HotReload\HotReload.cpp">
<Filter>src\Core\HotReload</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\HotReload\Win32\Win32HotReload.cpp">
<Filter>src\Core\HotReload\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Juliet.cpp">
<Filter>src\Core</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Logging\LogManager.cpp">
<Filter>src\Core\Logging</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Math\Math_Private.h">
<Filter>src\Core\Math</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Math\MathRound.cpp">
<Filter>src\Core\Math</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Memory\Allocator.cpp">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Memory\EngineArena.h">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Memory\MemoryArena.cpp">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Memory\MemoryArenaTests.cpp">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Memory\ScratchArena.cpp">
<Filter>src\Core\Memory</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Networking\NetworkPacket.cpp">
<Filter>src\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Networking\Socket.cpp">
<Filter>src\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Networking\SocketPlatformImpl.h">
<Filter>src\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Networking\TcpListener.cpp">
<Filter>src\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Networking\TcpSocket.cpp">
<Filter>src\Core\Networking</Filter>
</CustomBuild>
<CustomBuild Include="src\Core\Networking\Win32\Win32SocketPlatformImpl.cpp">
<Filter>src\Core\Networking\Win32</Filter>
</CustomBuild>
<CustomBuild Include="src\Engine\Engine.cpp">
<Filter>src\Engine</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12.h">
<Filter>src\Graphics\D3D12\AgilitySDK</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12compatibility.h">
<Filter>src\Graphics\D3D12\AgilitySDK</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12sdklayers.h">
<Filter>src\Graphics\D3D12\AgilitySDK</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12shader.h">
<Filter>src\Graphics\D3D12\AgilitySDK</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\D3D12TokenizedProgramFormat.hpp">
<Filter>src\Graphics\D3D12\AgilitySDK</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3d12video.h">
<Filter>src\Graphics\D3D12\AgilitySDK</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dcommon.h">
<Filter>src\Graphics\D3D12\AgilitySDK</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_barriers.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_check_feature_support.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_core.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_default.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_pipeline_state_stream.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_property_format_table.cpp">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_property_format_table.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_render_pass.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_resource_helpers.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_root_signature.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_state_object.h">
<Filter>src\Graphics\D3D12\AgilitySDK\d3dx12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\AgilitySDK\dxgiformat.h">
<Filter>src\Graphics\D3D12\AgilitySDK</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Buffer.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Buffer.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12CommandList.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12CommandList.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Common.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Common.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12DescriptorHeap.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12DescriptorHeap.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12GraphicsDevice.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12GraphicsDevice.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12GraphicsPipeline.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12GraphicsPipeline.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Includes.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12InternalTests.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12InternalTests.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12RenderPass.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12RenderPass.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Shader.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Shader.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12SwapChain.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12SwapChain.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Synchronization.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Synchronization.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Texture.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Texture.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Utils.cpp">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\D3D12\D3D12Utils.h">
<Filter>src\Graphics\D3D12</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\DebugDisplayRenderer.cpp">
<Filter>src\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\Graphics.cpp">
<Filter>src\Graphics</Filter>
</CustomBuild>
<CustomBuild Include="src\Graphics\GraphicsDevice.h">
<Filter>src\Graphics</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\Application">
<UniqueIdentifier>{5c7eaaef-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core">
<UniqueIdentifier>{6039c8dd-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include">
<UniqueIdentifier>{59f67477-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\Common">
<UniqueIdentifier>{ac29c266-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\Container">
<UniqueIdentifier>{ed394e4e-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\HAL\Display">
<UniqueIdentifier>{1cb0928e-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\HAL">
<UniqueIdentifier>{bcf269d1-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\HAL\DynLib">
<UniqueIdentifier>{fefb3f3a-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\HAL\Event">
<UniqueIdentifier>{54f5d918-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\HAL\Filesystem">
<UniqueIdentifier>{06b1338d-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\HAL\IO">
<UniqueIdentifier>{d111532d-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\HAL\Keyboard">
<UniqueIdentifier>{481eb40e-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\HAL\Mouse">
<UniqueIdentifier>{4c7d6b5f-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\HotReload">
<UniqueIdentifier>{fe4e9898-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\Logging">
<UniqueIdentifier>{02138187-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\Math">
<UniqueIdentifier>{87b853be-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\Memory">
<UniqueIdentifier>{d243d76b-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\Networking">
<UniqueIdentifier>{1f1cb326-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Core\Thread">
<UniqueIdentifier>{24f2aeeb-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Engine">
<UniqueIdentifier>{d881a52c-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="include\Graphics">
<UniqueIdentifier>{20496e7b-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\Application">
<UniqueIdentifier>{ab7ca14b-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core">
<UniqueIdentifier>{7d76d9f1-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src">
<UniqueIdentifier>{58a44f9c-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\Common">
<UniqueIdentifier>{f763d340-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL\Display">
<UniqueIdentifier>{82da0982-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL">
<UniqueIdentifier>{42e45efa-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL\Display\Win32">
<UniqueIdentifier>{3a37a82c-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL\DynLib\Win32">
<UniqueIdentifier>{966b00ac-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL\DynLib">
<UniqueIdentifier>{2a308679-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL\Event">
<UniqueIdentifier>{16e07e61-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL\Filesystem">
<UniqueIdentifier>{8dc5a7e5-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL\Filesystem\Win32">
<UniqueIdentifier>{37313215-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL\IO">
<UniqueIdentifier>{254581d9-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HAL\IO\Win32">
<UniqueIdentifier>{37523cd5-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HotReload">
<UniqueIdentifier>{35231af8-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\HotReload\Win32">
<UniqueIdentifier>{849dd795-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\Logging">
<UniqueIdentifier>{574d127d-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\Math">
<UniqueIdentifier>{d5fc938e-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\Memory">
<UniqueIdentifier>{8a7fc92e-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\Networking">
<UniqueIdentifier>{80be9352-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Core\Networking\Win32">
<UniqueIdentifier>{43aa9349-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Engine">
<UniqueIdentifier>{602a4b6b-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Graphics\D3D12\AgilitySDK">
<UniqueIdentifier>{8d3eff4b-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Graphics\D3D12">
<UniqueIdentifier>{41bbd51f-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Graphics">
<UniqueIdentifier>{d1949745-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Filter Include="src\Graphics\D3D12\AgilitySDK\d3dx12">
<UniqueIdentifier>{0fd13bb0-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
+30
View File
@@ -0,0 +1,30 @@
// =============================================================================
// Safe Casts
internal uint16 safe_cast_uint16(uint32 val)
{
AssertAlways(val <= max_uint16);
uint16 result = (uint16)val;
return result;
}
internal uint32 safe_cast_uint32(uint64 val)
{
AssertAlways(val <= max_uint32);
uint32 result = (uint32)val;
return result;
}
internal int32 safe_cast_int32(int64 val)
{
AssertAlways(val <= max_int32);
int32 result = (int32)val;
return result;
}
// =============================================================================
// Third party includes
#define STB_SPRINTF_IMPLEMENTATION
#define STB_SPRINTF_STATIC
#include "external/stb_sprintf/stb_sprintf.h"
+299
View File
@@ -0,0 +1,299 @@
#pragma once
#include <stdint.h>
// =============================================================================
// Third party includes
#define STB_SPRINTF_DECORATE(name) juliet_##name
#define STB_SPRINTF_STATIC
#include "external/stb_sprintf/stb_sprintf.h"
#define internal static
#define global static
#define local_persist static
#if (COMPILER_CLANG && OS_WINDOWS) || COMPILER_MSVC
# pragma section(".rdata$", read)
# define read_only __declspec(allocate(".rdata$"))
#elif (COMPILER_CLANG && OS_LINUX)
# define read_only __attribute__((section(".rodata")))
#else
// Note: not possible with GCC
# define read_only
#endif
#if COMPILER_CLANG || COMPILER_GCC
# define thread_static __thread
#elif COMPILER_MSVC
# define thread_static __declspec(thread)
#else
# error thread_static not defined for this compiler
#endif
// =============================================================================
// Units
#define KB(d) ((uint64)(d) << 10)
#define MB(d) ((uint64)(d) << 20)
#define GB(d) ((uint64)(d) << 30)
#define Million(n) ((n) * 1000000)
// =============================================================================
// Branch predictor hints
#if defined(__clang__)
# define Expect(expr, val) __builtin_expect((expr), (val))
#else
# define Expect(expr, val) (expr)
#endif
#define Likely(expr) Expect(expr, 1)
#define Unlikely(expr) Expect(expr, 0)
// =============================================================================
// Min Max Clamp
#define Min(A, B) (((A) < (B) ? (A) : (B)))
#define Max(A, B) (((A) > (B) ? (A) : (B)))
#define ClampTop(A, X) Min(A, X)
#define ClampBottom(X, B) Max(X, B)
#define Clamp(A, X, B) (((X) < (A)) ? (A) : ((X) > (B)) ? (B) : (X))
// =============================================================================
// Alignment
#if COMPILER_CLANG || COMPILER_GCC
# define AlignOf(Type) __alignof(Type)
#elif COMPILER_MSVC
# define AlignOf(Type) __alignof(Type)
#else
# error AlignOf not defined for this compiler
#endif
// =============================================================================
// For loop helpers
#define DeferLoop(begin, end) for (int _i_ = ((begin), 0); !_i_; ++_i_, (end))
#define EachIndex(it, count) (uint64 it = 0; it < (count); ++it)
#define EachNode(it, Type, first) (Type* it = first; it != 0; it = it->next)
// =============================================================================
// Memory Helpers
#define MemoryCopy(dst, src, size) memmove((dst), (src), (size))
#define MemorySet(dst, src, size) memset((dst), (src), (size))
#define MemoryCompare(a, b, size) memcmp((a), (b), (size))
#define MemoryZero(ptr, size) memset((ptr), 0, (size))
#define MemoryZeroStruct(s) MemoryZero((s), sizeof(*(s)))
#define MemoryZeroArray(arr) MemoryZero((arr), sizeof(arr))
#define MemoryMatch(a, b, size) (MemoryCompare((a), (b), (size)) == 0)
#define MemoryMatchStruct(a, b) MemoryMatch((a), (b), sizeof(*(a)))
#define MemoryMatchArray(a, b) MemoryMatch((a), (b), sizeof(a))
// =============================================================================
// Asserts
#if COMPILER_CLANG || COMPILER_GCC
# define Trap() __builtin_trap()
#elif COMPILER_MSVC
# define Trap() __debugbreak()
#else
# error Unknown trap intrinsic for this compiler
#endif
#define AssertAlways(cond) \
do \
{ \
if (!(cond)) \
{ \
Trap(); \
} \
} \
while (0)
#if BUILD_DEBUG
# define Assert(cond) AssertAlways(cond)
#else
# define Assert(cond) (void)(cond)
#endif
#define StaticAssert(cond, ID) global uint8 GLUE(ID, __LINE__)[(cond) ? 1 : -1]
// =============================================================================
// Linked List macro
// Linked list macro helpers
#define CheckNil(nil, ptr) ((ptr) == 0 || (ptr) == nil)
#define SetNil(nil, ptr) ((ptr) = nil)
// Doubly-linked-lists
// clang-format off
#define DLL_insert_next_prev_zero(nil, first, last, ptr, node, next, previous) \
(CheckNil(nil, first) ? ((first) = (last) = (node), SetNil(nil, (node)->next), SetNil(nil, (node)->previous)) \
: CheckNil(nil, ptr) ? ((node)->next = (first), (first)->previous = (node), (first) = (node), SetNil(nil, (node)->previous)) \
: ((ptr) == (last)) ? ((last)->next = (node), (node )->previous = (last), (last ) = (node), SetNil(nil, (node)->next)) \
: (((CheckNil(nil, ptr) && CheckNil(nil, (ptr)->next)) ? (0) : ((ptr)->next->previous = (node))), \
((node)->next = (ptr)->next), ((ptr)->next = (node)), ((node)->previous = (ptr))))
#define DLL_push_back_next_prev_zero(nil, first, last, node, next, previous) \
DLL_insert_next_prev_zero(nil, first, last, last, node, next, previous)
#define DLL_remove_next_prev_zero(nil, first, last, node, next, previous) \
(((node) == (first) ? (first) = (node)->next : (0)), ((node) == (last) ? (last) = (last)->previous : (0)), \
(CheckNil(nil, (node)->previous) ? (0) : ((node)->previous->next = (node)->next)), \
(CheckNil(nil, (node)->next) ? (0) : ((node)->next->previous = (node)->previous)))
// clang-format on
// Single link double head lists (= queues)
#define SLL_queue_push_next_zero(nil, first, last, node, next) \
(CheckNil(nil, first) ? ((first) = (last) = (node), SetNil(nil, (node)->next)) \
: ((last)->next = (node), (last) = (node), SetNil(nil, (node)->next)))
// Single (link & head) lists (= stacks)
#define SLL_stack_push_next(first, node, next) ((node)->next = (first), (first) = (node))
#define SLL_stack_pop_next(first, next) ((first) = (first)->next);
// Double linked list helpers
#define DLL_push_back_next_prev(first, last, node, next, previous) \
DLL_push_back_next_prev_zero(0, first, last, node, next, previous)
#define DLL_remove_next_prev(first, last, node, next, prev) DLL_remove_next_prev_zero(0, first, last, node, next, prev)
#define DLL_push_back(first, last, node) DLL_push_back_next_prev_zero(0, first, last, node, next, previous)
#define DLL_remove(first, last, node) DLL_remove_next_prev_zero(0, first, last, node, next, previous)
// Queue helpers
#define SLL_queue_push(first, last, node) SLL_queue_push_next_zero(0, first, last, node, next)
// Stack helpers
#define SLL_stack_push(first, node) SLL_stack_push_next(first, node, next)
#define SLL_stack_pop(first) SLL_stack_pop_next(first, next)
// =============================================================================
// Helpers
#define AlignPow2(x, align) (((x) + (align) - 1) & (~((align) - 1)))
#define Stringify_(S) #S
#define Stringify(S) Stringify_(S)
#define GLUE_(A, B) A##B
#define GLUE(A, B) GLUE_(A, B)
#define ArrayCount(arr) (sizeof(arr) / sizeof((arr)[0]))
#if ARCH_64BIT
# define IntFromPtr(ptr) ((uint64)(ptr))
#else
# error Missing pointer to integer cast for this architecture
#endif
#define PtrFromInt(i) (void*)(i)
// =============================================================================
// Core Types
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef float float32;
typedef double float64;
typedef int8 bool8;
typedef int16 bool16;
typedef int32 bool32;
typedef int64 bool64;
// =============================================================================
// Core Constants
global uint8 max_uint8 = 0xFF;
global uint16 max_uint16 = 0xFFFF;
global uint32 max_uint32 = 0xFFFFFFFF;
global uint64 max_uint64 = 0xFFFFFFFFFFFFFFFFll;
global int8 max_int8 = (int8)0x7F;
global int16 max_int16 = (int16)0x7FFF;
global int32 max_int32 = (int32)0x7FFFFFFF;
global int64 max_int64 = (int64)0x7FFFFFFFFFFFFFFFll;
global const uint32 bitmask1 = 0x00000001;
global const uint32 bitmask2 = 0x00000003;
global const uint32 bitmask3 = 0x00000007;
global const uint32 bitmask4 = 0x0000000f;
global const uint32 bitmask5 = 0x0000001f;
global const uint32 bitmask6 = 0x0000003f;
global const uint32 bitmask7 = 0x0000007f;
global const uint32 bitmask8 = 0x000000ff;
global const uint32 bitmask9 = 0x000001ff;
global const uint32 bitmask10 = 0x000003ff;
global const uint32 bitmask11 = 0x000007ff;
global const uint32 bitmask12 = 0x00000fff;
global const uint32 bitmask13 = 0x00001fff;
global const uint32 bitmask14 = 0x00003fff;
global const uint32 bitmask15 = 0x00007fff;
global const uint32 bitmask16 = 0x0000ffff;
global const uint32 bitmask17 = 0x0001ffff;
global const uint32 bitmask18 = 0x0003ffff;
global const uint32 bitmask19 = 0x0007ffff;
global const uint32 bitmask20 = 0x000fffff;
global const uint32 bitmask21 = 0x001fffff;
global const uint32 bitmask22 = 0x003fffff;
global const uint32 bitmask23 = 0x007fffff;
global const uint32 bitmask24 = 0x00ffffff;
global const uint32 bitmask25 = 0x01ffffff;
global const uint32 bitmask26 = 0x03ffffff;
global const uint32 bitmask27 = 0x07ffffff;
global const uint32 bitmask28 = 0x0fffffff;
global const uint32 bitmask29 = 0x1fffffff;
global const uint32 bitmask30 = 0x3fffffff;
global const uint32 bitmask31 = 0x7fffffff;
global const uint32 bitmask32 = 0xffffffff;
global const uint32 bit1 = (1 << 0);
global const uint32 bit2 = (1 << 1);
global const uint32 bit3 = (1 << 2);
global const uint32 bit4 = (1 << 3);
global const uint32 bit5 = (1 << 4);
global const uint32 bit6 = (1 << 5);
global const uint32 bit7 = (1 << 6);
global const uint32 bit8 = (1 << 7);
global const uint32 bit9 = (1 << 8);
global const uint32 bit10 = (1 << 9);
global const uint32 bit11 = (1 << 10);
global const uint32 bit12 = (1 << 11);
global const uint32 bit13 = (1 << 12);
global const uint32 bit14 = (1 << 13);
global const uint32 bit15 = (1 << 14);
global const uint32 bit16 = (1 << 15);
global const uint32 bit17 = (1 << 16);
global const uint32 bit18 = (1 << 17);
global const uint32 bit19 = (1 << 18);
global const uint32 bit20 = (1 << 19);
global const uint32 bit21 = (1 << 20);
global const uint32 bit22 = (1 << 21);
global const uint32 bit23 = (1 << 22);
global const uint32 bit24 = (1 << 23);
global const uint32 bit25 = (1 << 24);
global const uint32 bit26 = (1 << 25);
global const uint32 bit27 = (1 << 26);
global const uint32 bit28 = (1 << 27);
global const uint32 bit29 = (1 << 28);
global const uint32 bit30 = (1 << 29);
global const uint32 bit31 = (1 << 30);
global const uint32 bit32 = (1 << 31);
// =============================================================================
// Safe Casts
internal uint16 safe_cast_uint16(uint32 val);
internal uint32 safe_cast_uint32(uint64 val);
internal int32 safe_cast_int32(int64 val);
// =============================================================================
// Time functions
internal uint64 now_time_us();
internal void sleep_ms(uint32 ms);
+193
View File
@@ -0,0 +1,193 @@
// =============================================================================
// Arena Functions
#include "core/core.h"
internal Arena* arena_alloc_internal(ArenaParams* params)
{
uint64 committed = params->commit_size;
uint64 reserved = params->reserve_size;
// Initial block
void* base = NULL;
if (params->flags & ArenaFlag_LargePages)
{
uint64 page_size = get_system_info()->large_page_size;
reserved = AlignPow2(reserved, page_size);
committed = AlignPow2(committed, page_size);
base = reserve_memory_large(reserved);
commit_memory_large(base, committed);
}
else
{
uint64 page_size = get_system_info()->page_size;
reserved = AlignPow2(reserved, page_size);
committed = AlignPow2(committed, page_size);
base = reserve_memory(reserved);
commit_memory(base, committed);
}
if (Unlikely(base == 0))
{
abort_self(1);
}
Arena* arena = base;
arena->current = arena;
arena->commit_size = params->commit_size;
arena->reserve_size = params->reserve_size;
arena->base_position = 0;
arena->position = ARENA_HEADER_SIZE;
arena->committed = committed;
arena->reserved = reserved;
#if BUILD_DEBUG
arena->alloc_file = params->alloc_file;
arena->alloc_line = params->alloc_line;
arena->name = params->name;
#endif
return arena;
}
internal void arena_release(Arena* arena)
{
for (Arena *p = arena->current, *prev = 0; p != 0; p = prev)
{
prev = p->previous;
release_memory(p, p->reserved);
}
}
// =============================================================================
// Arena Push/Pop core Functions
internal void* arena_push(Arena* arena, uint64 size, uint64 align, bool32 zero)
{
Arena* current = arena->current;
uint64 pos_pre = AlignPow2(current->position, align);
uint64 pos_post = pos_pre + size;
uint64 size_to_zero = 0;
if (zero)
{
size_to_zero = Min(current->committed, pos_post) - pos_pre;
}
if (current->reserved < pos_post && !(arena->flags & ArenaFlag_NoChain))
{
Arena* new_block = 0;
if (new_block == 0)
{
uint64 reserved_size = arena->reserve_size;
uint64 committed_size = arena->commit_size;
if (size + ARENA_HEADER_SIZE > reserved_size)
{
reserved_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
committed_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
}
new_block = arena_alloc(.reserve_size = reserved_size, .commit_size = committed_size, .flags = arena->flags,
#if BUILD_DEBUG
.alloc_file = arena->alloc_file, .alloc_line = arena->alloc_line
#endif
);
size_to_zero = 0;
}
else
{
size_to_zero = size;
}
new_block->base_position = current->base_position + current->reserved;
SLL_stack_push_next(arena->current, new_block, previous);
current = new_block;
pos_pre = AlignPow2(current->position, align);
pos_post = pos_pre + size;
}
// commit new pages if needed
if (current->committed < pos_post)
{
uint64 committed_past_aligned = pos_post + current->commit_size - 1;
committed_past_aligned -= committed_past_aligned % current->commit_size;
uint64 committed_past_clamped = ClampTop(committed_past_aligned, current->reserved);
uint64 committed_size = committed_past_clamped - current->committed;
uint8* committed_ptr = (uint8*)current + current->committed;
if (current->flags & ArenaFlag_LargePages)
{
commit_memory_large(committed_ptr, committed_size);
}
else
{
commit_memory(committed_ptr, committed_size);
}
current->committed = committed_past_clamped;
}
void* result = 0;
if (current->committed >= pos_post)
{
result = (uint8*)current + pos_pre;
current->position = pos_post;
MemoryZero(result, size_to_zero);
}
if (Unlikely(result == 0))
{
abort_self(1);
}
return result;
}
internal uint64 arena_pos(Arena* arena)
{
Arena* current = arena->current;
uint64 position = current->base_position + current->position;
return position;
}
internal void arena_pop_to(Arena* arena, uint64 position)
{
uint64 clamped_pos = ClampBottom(ARENA_HEADER_SIZE, position);
Arena* current = arena->current;
for (Arena* previous = 0; current->base_position >= clamped_pos; current = previous)
{
previous = current->previous;
release_memory(current, current->reserved);
}
arena->current = current;
uint64 new_position = clamped_pos - current->base_position;
AssertAlways(new_position <= current->position);
current->position = new_position;
}
internal void arena_clear(Arena* arena)
{
arena_pop_to(arena, 0);
}
internal void arena_pop(Arena* arena, uint64 amount)
{
uint64 pos_old = arena_pos(arena);
Assert(pos_old > amount);
uint64 pos_new = pos_old - amount;
arena_pop_to(arena, pos_new);
}
// Temp arena
internal TempArena temp_begin(Arena* arena)
{
uint64 position = arena_pos(arena);
TempArena temp = { arena, position };
return temp;
}
internal void temp_end(TempArena temp)
{
arena_pop_to(temp.arena, temp.position);
}
+88
View File
@@ -0,0 +1,88 @@
#pragma once
#define ARENA_HEADER_SIZE 128
// =============================================================================
// Arena Types
typedef uint64 ArenaFlags;
enum
{
ArenaFlag_NoChain = (1 << 0),
ArenaFlag_LargePages = (1 << 1),
};
typedef struct ArenaParams ArenaParams;
struct ArenaParams
{
ArenaFlags flags;
uint64 commit_size;
uint64 reserve_size;
char* alloc_file;
int alloc_line;
char* name;
};
typedef struct Arena Arena;
struct Arena
{
Arena* previous;
Arena* current;
ArenaFlags flags;
uint64 base_position;
uint64 position;
uint64 commit_size;
uint64 committed;
uint64 reserve_size;
uint64 reserved;
#if BUILD_DEBUG
char* alloc_file;
int alloc_line;
char* name;
#endif
};
StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check);
typedef struct TempArena TempArena;
struct TempArena
{
Arena* arena;
uint64 position;
};
// =============================================================================
// Arena Functions
global uint64 arena_default_reserve_size = MB(64);
global uint64 arena_default_commit_size = KB(64);
internal Arena* arena_alloc_internal(ArenaParams* params);
#define arena_alloc(...) \
arena_alloc_internal(&(ArenaParams){ .commit_size = arena_default_commit_size, \
.reserve_size = arena_default_reserve_size, \
.alloc_file = __FILE__, \
.alloc_line = __LINE__, \
__VA_ARGS__ })
internal void arena_release(Arena* arena);
// Arena Push/Pop core Functions
internal void* arena_push(Arena* arena, uint64 size, uint64 align, bool32 zero);
internal uint64 arena_pos(Arena* arena);
internal void arena_pop_to(Arena* arena, uint64 position);
internal void arena_clear(Arena* arena);
internal void arena_pop(Arena* arena, uint64 amount);
// Temp arena
internal TempArena temp_begin(Arena* arena);
internal void temp_end(TempArena temp);
// =============================================================================
// Push Helper Macros
#define push_array_no_zero_aligned(arr, Type, count, align) \
(Type*)arena_push((arr), sizeof(Type) * (count), (align), (0))
#define push_array_aligned(arr, Type, count, align) (Type*)arena_push((arr), sizeof(Type) * (count), (align), (1))
#define push_array_no_zero(arr, Type, count) push_array_no_zero_aligned(arr, Type, count, Max(8, AlignOf(Type)))
#define push_array(arr, Type, count) push_array_aligned(arr, Type, count, Max(8, AlignOf(Type)))
+98
View File
@@ -0,0 +1,98 @@
#pragma once
// ========================================================================== //
// Compiler defines | Clang
#if defined(__clang__)
# define COMPILER_CLANG 1
# if defined(_WIN32)
# define OS_WINDOWS 1
# else
# error Compiler / OS combo is not supported
# endif
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
# define ARCH_X64 1
# else
# error Architecture not supported
# endif
#elif defined(_MSC_VER)
# define COMPILER_MSVC 1
# if defined(_WIN32)
# define OS_WINDOWS 1
# else
# error Compiler / OS combo is not supported
# endif
# if defined(_M_AMD64)
# define ARCH_X64 1
# else
# error Architecture not supported
# endif
#else
# error Compiler not supported.
#endif
// ========================================================================== //
// Architecture
#if defined(ARCH_X64)
# define ARCH_64BIT 1
#endif
// ========================================================================== //
// Language
#if defined(__cplusplus)
# define LANG_CPP 1
#else
# define LANG_C 1
#endif
// ========================================================================== //
// Build Options
#if !defined(BUILD_DEBUG)
# define BUILD_DEBUG 1
#endif
// ========================================================================== //
// Zeroing unused Options
#if !defined(ARCH_X64)
# define ARCH_X64 0
#endif
#if !defined(ARCH_X86)
# define ARCH_X86 0
#endif
#if !defined(COMPILER_CLANG)
# define COMPILER_CLANG 0
#endif
#if !defined(COMPILER_MSVC)
# define COMPILER_MSVC 0
#endif
#if !defined(OS_WINDOWS)
# define OS_WINDOWS 0
#endif
#if !defined(LANG_CPP)
# define LANG_CPP 0
#endif
#if !defined(LANG_C)
# define LANG_C 0
#endif
#if !ARCH_X64
# error Trying to build unsupported architecture. Only x64 is supported.
#endif
+13
View File
@@ -0,0 +1,13 @@
internal void entry_point();
internal void main_thread_core_entry_point(int argc, char** argv)
{
ThreadNameF("main_thread");
entry_point();
}
internal bool32 update()
{
bool32 result = frame();
return result;
}
+4
View File
@@ -0,0 +1,4 @@
#pragma once
internal void main_thread_core_entry_point(int argc, char** argv);
internal bool32 update();
+26
View File
@@ -0,0 +1,26 @@
// =============================================================================
// Hash Functions
#pragma push_macro("__cpuid")
#undef __cpuid
#if !defined(XXH_IMPLEMENTATION)
# define XXH_INLINE_ALL
# define XXH_IMPLEMENTATION
# define XXH_STATIC_LINKING_ONLY
# include "external/xxHash/xxhash.h"
#endif
#pragma pop_macro("__cpuid")
#define HASH_SEED 4259
internal uint64 uint64_hash_from_string(String8 string)
{
uint64 result = uint64_hash_from_seed_string(HASH_SEED, string);
return result;
}
internal uint64 uint64_hash_from_seed_string(uint64 seed, String8 string)
{
uint64 result = XXH3_64bits_withSeed(string.str, string.size, seed);
return result;
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
// =============================================================================
// Hash Functions
internal uint64 uint64_hash_from_string(String8 string);
internal uint64 uint64_hash_from_seed_string(uint64 seed, String8 string);
+20
View File
@@ -0,0 +1,20 @@
// clang-format off
#include "core.c"
#include "core_memory.c"
#include "core_arena.c"
#include "core_math.c"
#include "core_string.c"
#include "core_hash.c"
#include "core_system.c"
#include "core_threads.c"
#include "core_thread_context.c"
#include "core_processes.c"
#include "core_markup.c"
#include "core_entry_point.c"
// clang-format on
#if OS_WINDOWS
# include "win32/core/win32_core.c"
#elif
# error Unsupported OS
#endif
+24
View File
@@ -0,0 +1,24 @@
#pragma once
// clang-format off
#include "core_defines.h"
#include "core.h"
#include "core_memory.h"
#include "core_processes.h"
#include "core_arena.h"
#include "core_math.h"
#include "core_string.h"
#include "core_hash.h"
#include "core_system.h"
#include "core_threads.h"
#include "core_thread_context.h"
#include "core_markup.h"
#include "core_entry_point.h"
// clang-format on
#if OS_WINDOWS
# include "win32/core/win32_core.h"
#elif
# error Unsupported OS
#endif
File renamed without changes.
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#define ThreadNameF(...) (set_thread_namef(__VA_ARGS__))
+33
View File
@@ -0,0 +1,33 @@
// =============================================================================
// Functions
// =============================================================================
// Vectors
internal Vector2f vector_2f(float32 x, float32 y)
{
Vector2f v = { x, y };
return v;
}
// =============================================================================
// Rectangles
internal Rect2f rect_2f(Vector2f min, Vector2f max)
{
Rect2f rect = { min, max };
return rect;
}
internal bool32 contains_2f(Rect2f rect, Vector2f x)
{
bool32 cond = (rect.min.x <= x.x && x.x < rect.max.x && rect.min.y <= x.y && x.y < rect.max.y);
return cond;
}
internal Vector2f dimension_2f(Rect2f rect)
{
Vector2f dim = { ((rect.max.x > rect.min.x) ? (rect.max.x - rect.min.x) : 0),
((rect.max.y > rect.min.y) ? (rect.max.y - rect.min.y) : 0) };
return dim;
}
+70
View File
@@ -0,0 +1,70 @@
#pragma once
// =============================================================================
// Vector Types
typedef union Vector2f Vector2f;
union Vector2f
{
struct
{
float32 x;
float32 y;
};
float32 v[2];
};
typedef union Vector2i Vector2i;
union Vector2i
{
struct
{
int32 x;
int32 y;
};
int32 v[2];
};
// =============================================================================
// Rect Types
typedef union Rect2f Rect2f;
union Rect2f
{
struct
{
Vector2f min;
Vector2f max;
};
struct
{
Vector2f p0;
Vector2f p1;
};
struct
{
float32 x0;
float32 y0;
float32 x1;
float32 y1;
};
Vector2f v[2];
};
// =============================================================================
// Functions
// =============================================================================
// Vectors
#define vector2f(x, y) vector_2f((x), (y))
internal Vector2f vector_2f(float32 x, float32 y);
// =============================================================================
// Rectangles
#define rect2f(min, max) rect_2f((min), (max))
#define rect2fp(x, y, z, w) rect2f(vector2f((x), (y)), vector2f((z), (w)))
internal Rect2f rect_2f(Vector2f min, Vector2f max);
internal bool32 contains_2f(Rect2f rect, Vector2f x);
internal Vector2f dimension_2f(Rect2f rect);
+1
View File
@@ -0,0 +1 @@
// OS DEPEND see os/core/os_core.c
+8
View File
@@ -0,0 +1,8 @@
#pragma once
internal void* reserve_memory(uint64 size);
internal bool32 commit_memory(void* ptr, uint64 size);
internal void* reserve_memory_large(uint64 size);
internal bool32 commit_memory_large(void* ptr, uint64 size);
internal void decommit_memory(void* ptr, uint64 size);
internal void release_memory(void* ptr, uint64 size);
File renamed without changes.
+6
View File
@@ -0,0 +1,6 @@
#pragma once
// =============================================================================
// Aborting (os dependent see os/core/os_core.c)
internal void abort_self(uint64 exit_code);
+395
View File
@@ -0,0 +1,395 @@
#ifndef MEM_STATIC
# define MEM_STATIC
# define MEM_API static inline
# include "external/memfun/memfun.h"
#endif
// =============================================================================
// String conversions
internal bool32 char_is_upper(uint8 c)
{
return ('A' <= c && c <= 'Z');
}
internal bool32 char_is_lower(uint8 c)
{
return ('a' <= c && c <= 'z');
}
internal bool32 char_is_slash(uint8 c)
{
return (c == '/' || c == '\\');
}
internal uint8 lower_from_char(uint8 c)
{
if (char_is_upper(c))
{
c += ('a' - 'A');
}
return c;
}
internal uint8 upper_from_char(uint8 c)
{
if (char_is_lower(c))
{
c += ('A' - 'a');
}
return c;
}
internal uint8 correct_slash_from_char(uint8 c)
{
if (char_is_slash(c))
{
c = '/';
}
return c;
}
// ========================================================================== //
// String Length
internal uint64 cstring8_length(uint8* cstring)
{
uint64 length = 0;
if (cstring)
{
uint8* p = cstring;
for (; *p != '\0'; ++p)
;
length = (uint64)(p - cstring);
}
return length;
}
internal uint64 cstring16_length(uint16* cstring)
{
uint64 length = 0;
if (cstring)
{
uint16* p = cstring;
for (; *p != '\0'; ++p)
;
length = (uint64)(p - cstring);
}
return length;
}
internal uint64 cstring32_length(uint32* cstring)
{
uint64 length = 0;
if (cstring)
{
uint32* p = cstring;
for (; *p != '\0'; ++p)
;
length = (uint64)(p - cstring);
}
return length;
}
// ========================================================================== //
// String Constructor
internal String8 str8(uint8* str, uint64 size)
{
String8 result = { str, size };
return result;
}
internal String8 str8_zero()
{
String8 result = { 0 };
return result;
}
internal String16 str16(uint16* str, uint64 size)
{
String16 result = { str, size };
return result;
}
internal String16 str16_zero()
{
String16 result = { 0 };
return result;
}
internal String8 str8_cstring(const char* cstring)
{
String8 result = { (uint8*)cstring, cstring8_length((uint8*)cstring) };
return result;
}
internal String16 str16_cstring(uint16* cstring)
{
String16 result = { (uint16*)cstring, cstring16_length((uint16*)cstring) };
return result;
}
internal String32 str32_cstring(uint32* cstring)
{
String32 result = { (uint32*)cstring, cstring32_length((uint32*)cstring) };
return result;
}
// =============================================================================
// String Matching
internal bool32 str8_match(String8 a, String8 b, StringMatchFlags flags)
{
bool32 result = 0;
if (a.size == b.size && flags == 0)
{
result = MemIsEqual(a.str, b.str, b.size);
}
else if (a.size == b.size && flags == StringMatchFlag_CaseInsensitive)
{
result = MemCompareI(a.str, b.str, a.size) == 0;
}
else if (a.size == b.size || (flags * StringMatchFlag_RightSideSloppy))
{
bool32 case_insensitive = (flags & StringMatchFlag_CaseInsensitive);
bool32 slash_insensitive = (flags & StringMatchFlag_SlashInsensitive);
uint64 size = Min(a.size, b.size);
result = 1;
for EachIndex(i, size)
{
uint8 at = a.str[i];
uint8 bt = b.str[i];
if (case_insensitive)
{
at = upper_from_char(at);
bt = upper_from_char(bt);
}
if (slash_insensitive)
{
at = correct_slash_from_char(at);
bt = correct_slash_from_char(bt);
}
if (at != bt)
{
result = 0;
break;
}
}
}
return result;
}
// =============================================================================
// String Formatting & Copying
internal String8 str8_copy(Arena* arena, String8 in)
{
String8 str;
str.size = in.size;
str.str = push_array_no_zero(arena, uint8, str.size + 1);
MemoryCopy(str.str, in.str, in.size);
str.str[str.size] = '\0';
return str;
}
internal String8 str8fv(Arena* arena, char* fmt, va_list args)
{
va_list args2;
va_copy(args2, args);
uint32 needed_bytes = juliet_vsnprintf(0, 0, fmt, args) + 1;
String8 result = { 0 };
result.str = push_array_no_zero(arena, uint8, needed_bytes);
result.size = juliet_vsnprintf((char*)result.str, needed_bytes, fmt, args2);
result.str[result.size] = '\0';
va_end(args2);
return result;
}
// =============================================================================
// Unicode String Encode/Decode
read_only global uint8 utf8_class[32] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 5,
};
internal UnicodeDecode utf8_decode(uint8* str, uint64 max)
{
UnicodeDecode result = { 1, max_uint32 };
uint8 byte = str[0];
uint8 byte_class = utf8_class[byte >> 3];
switch (byte_class)
{
case 1:
{
result.codepoint = byte;
break;
}
case 2:
{
if (1 < max)
{
uint8 cont_byte = str[1];
if (utf8_class[cont_byte >> 3] == 0)
{
result.codepoint = (byte & bitmask5) << 6;
result.codepoint |= (cont_byte & bitmask6);
result.increment = 2;
}
}
break;
}
case 3:
{
if (2 < max)
{
uint8 cont_byte[2] = { str[1], str[2] };
if (utf8_class[cont_byte[0] >> 3] == 0 && utf8_class[cont_byte[1] >> 3] == 0)
{
result.codepoint = (byte & bitmask4) << 12;
result.codepoint |= ((cont_byte[0] & bitmask6) << 6);
result.codepoint |= (cont_byte[1] & bitmask6);
result.increment = 3;
}
}
break;
}
case 4:
{
if (3 < max)
{
uint8 cont_byte[3] = { str[1], str[2], str[3] };
if (utf8_class[cont_byte[0] >> 3] == 0 && utf8_class[cont_byte[1] >> 3] == 0 && utf8_class[cont_byte[2] >> 3] == 0)
{
result.codepoint = (byte & bitmask3) << 18;
result.codepoint |= ((cont_byte[0] & bitmask6 << 12));
result.codepoint |= ((cont_byte[1] & bitmask6) << 6);
result.codepoint |= (cont_byte[2] & bitmask6);
result.increment = 4;
}
}
break;
}
}
return result;
}
internal UnicodeDecode utf16_decode(uint16* str, uint64 max)
{
UnicodeDecode result = { 1, max_uint32 };
result.codepoint = str[0];
result.increment = 1;
if (max > 1 && 0xD800 <= str[0] && str[0] < 0xDC00 && 0xDC00 <= str[1] && str[1] < 0xE000)
{
result.codepoint = ((str[0] - 0xD800) << 10) | ((str[1] - 0xDC00) + 0x10000);
result.increment = 2;
}
return result;
}
internal uint32 utf8_encode(uint8* str, uint32 codepoint)
{
uint32 increment = 0;
if (codepoint <= 0x7f)
{
str[0] = (uint8)codepoint;
increment = 1;
}
else if (codepoint <= 0x7FF)
{
str[0] = (bitmask2 << 6) | ((codepoint >> 6) & bitmask5);
str[1] = bit8 | (codepoint & bitmask6);
increment = 2;
}
else if (codepoint <= 0xFFFF)
{
str[0] = (bitmask3 << 5) | ((codepoint >> 12) & bitmask4);
str[1] = bit8 | ((codepoint >> 6) & bitmask6);
str[2] = bit8 | (codepoint & bitmask6);
increment = 3;
}
else if (codepoint <= 0x10FFFF)
{
str[0] = (bitmask4 << 4) | ((codepoint >> 18) & bitmask3);
str[1] = bit8 | ((codepoint >> 12) & bitmask6);
str[2] = bit8 | (codepoint >> 6 & bitmask6);
str[3] = bit8 | (codepoint & bitmask6);
increment = 4;
}
else
{
str[0] = '?';
increment = 1;
}
return increment;
}
internal uint32 utf16_encode(uint16* str, uint32 codepoint)
{
uint32 increment = 1;
if (codepoint == max_uint32)
{
str[0] = (uint16)'?';
}
else if (codepoint < 0x10000)
{
str[0] = (uint16)codepoint;
}
else
{
uint32 v = codepoint - 0x10000;
str[0] = safe_cast_uint16(0xD800 + (v >> 10));
str[1] = safe_cast_uint16(0xDC00 + (v & bitmask10));
increment = 2;
}
return increment;
}
// =============================================================================
// Unicode String Conversions
internal String8 str8_from_16(Arena* arena, String16 in)
{
String8 result = str8_zero();
if (in.size)
{
uint64 capacity = in.size * 3;
uint8* str = push_array_no_zero(arena, uint8, capacity + 1);
uint16* ptr = in.str;
uint16* opl = ptr + in.size;
uint64 size = 0;
UnicodeDecode consume;
for (; ptr < opl; ptr += consume.increment)
{
consume = utf16_decode(ptr, opl - ptr);
size += utf8_encode(str + size, consume.codepoint);
}
str[size] = '\0';
arena_pop(arena, (capacity - size));
result = str8(str, size);
}
return result;
}
internal String16 str16_from_8(Arena* arena, String8 in)
{
String16 result = str16_zero();
if (in.size)
{
uint64 capacity = in.size * 2;
uint16* str = push_array_no_zero(arena, uint16, capacity + 1);
uint8* ptr = in.str;
uint8* opl = ptr + in.size;
uint64 size = 0;
UnicodeDecode consume;
for (; ptr < opl; ptr += consume.increment)
{
consume = utf8_decode(ptr, opl - ptr);
size += utf16_encode(str + size, consume.codepoint);
}
str[size] = '\0';
arena_pop(arena, (capacity - size) * 2);
result = str16(str, size);
}
return result;
}
+104
View File
@@ -0,0 +1,104 @@
#pragma once
// =============================================================================
// String Types
typedef struct String8 String8;
struct String8
{
uint8* str;
uint64 size;
};
typedef struct String16 String16;
struct String16
{
uint16* str;
uint64 size;
};
typedef struct String32 String32;
struct String32
{
uint32* str;
uint64 size;
};
// =============================================================================
// String Matching Types
typedef uint32 StringMatchFlags;
enum
{
StringMatchFlag_CaseInsensitive = (1 << 0),
StringMatchFlag_RightSideSloppy = (1 << 1),
StringMatchFlag_SlashInsensitive = (1 << 2),
};
// =============================================================================
// Unicode decoding Types
typedef struct UnicodeDecode UnicodeDecode;
struct UnicodeDecode
{
uint32 increment;
uint32 codepoint;
};
// =============================================================================
// String conversions
internal bool32 char_is_upper(uint8 c);
internal bool32 char_is_lower(uint8 c);
internal bool32 char_is_slash(uint8 c);
internal uint8 lower_from_char(uint8 c);
internal uint8 upper_from_char(uint8 c);
internal uint8 correct_slash_from_char(uint8 c);
// =============================================================================
// String Length
internal uint64 cstring8_length(uint8* cstring);
internal uint64 cstring16_length(uint16* cstring);
internal uint64 cstring32_length(uint32* cstring);
// =============================================================================
// String Constructor
#define str8_literal(S) str8((uint8*)(S), sizeof(S) - 1)
#define str8_struct(S) str8((uint8*)(S), sizeof(*(S)))
internal String8 str8(uint8* str, uint64 size);
internal String8 str8_zero();
internal String16 str16(uint16* str, uint64 size);
internal String16 str16_zero();
internal String8 str8_cstring(const char* cstring);
internal String16 str16_cstring(uint16* cstring);
internal String32 str32_cstring(uint32* cstring);
// =============================================================================
// String Matching
internal bool32 str8_match(String8 a, String8 b, StringMatchFlags flags);
#define str8_matchi(a, b) str8_match(a, b, StringMatchFlag_CaseInsensitive)
// =============================================================================
// String Formatting & Copying
internal String8 str8_copy(Arena* arena, String8 in);
internal String8 str8fv(Arena* arena, char* fmt, va_list args);
// =============================================================================
// Unicode String Encode/Decode
internal UnicodeDecode utf8_decode(uint8* str, uint64 max);
internal UnicodeDecode utf16_decode(uint16* str, uint64 max);
internal uint32 utf8_encode(uint8* str, uint32 codepoint);
internal uint32 utf16_encode(uint16* str, uint32 codepoint);
// =============================================================================
// Unicode String Conversions
internal String8 str8_from_16(Arena* arena, String16 in);
internal String16 str16_from_8(Arena* arena, String8 in);
+1
View File
@@ -0,0 +1 @@
// OS DEPEND see os/core/os_core.c
+11
View File
@@ -0,0 +1,11 @@
#pragma once
typedef struct SystemInfo SystemInfo;
struct SystemInfo
{
uint64 page_size;
uint64 large_page_size;
String8 machine_name;
};
internal SystemInfo* get_system_info();
+71
View File
@@ -0,0 +1,71 @@
// =============================================================================
// Globals
thread_static TCTX* tctx_thread_local;
// =============================================================================
// Thread Context Functions
// Thread Context allocation
internal TCTX* tctx_alloc()
{
#if BUILD_DEBUG
thread_static static char name[2][1024];
juliet_snprintf(name[0], sizeof(name[0]), "Scratch/0[TID:%u]", tid());
juliet_snprintf(name[1], sizeof(name[1]), "Scratch/1[TID:%u]", tid());
Arena* arena_0 = arena_alloc(.name = name[0]);
Arena* arena_1 = arena_alloc(.name = name[1]);
#else
Arena* arena_0 = arena_alloc();
Arena* arena_1 = arena_alloc();
#endif
TCTX* tctx = push_array(arena_0, TCTX, 1);
tctx->arenas[0] = arena_0;
tctx->arenas[1] = arena_1;
return tctx;
}
internal void tctx_release(TCTX* tctx)
{
arena_release(tctx->arenas[1]);
arena_release(tctx->arenas[0]);
}
internal void tctx_select(TCTX* tctx)
{
tctx_thread_local = tctx;
}
internal TCTX* tctx_selected()
{
return tctx_thread_local;
}
internal Arena* tctx_get_scratch(Arena** conflicts, uint64 count)
{
TCTX* tctx = tctx_selected();
Arena* result = 0;
Arena** arena_ptr = tctx->arenas;
for (uint64 i = 0; i < ArrayCount(tctx->arenas); ++i, ++arena_ptr)
{
Arena** conflict_ptr = conflicts;
bool32 has_conflict = 0;
for (uint64 j = 0; j < count; ++j, ++conflict_ptr)
{
if (*arena_ptr == *conflict_ptr)
{
has_conflict = 1;
break;
}
}
if (!has_conflict)
{
result = *arena_ptr;
break;
}
}
return result;
}
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
#define scratch_end(scratch) temp_end(scratch)
Loaded 100 of 400 files, more files were not shown because too many files have changed in this diff. Show more