Day 33 complete.

Man...
This commit is contained in:
Edward R. Gonzalez 2023-10-11 00:56:16 -04:00
parent 22dfe4165b
commit 7958fabd00
32 changed files with 881 additions and 730 deletions

View File

@ -83,5 +83,6 @@
"C_Cpp.files.exclude": { "C_Cpp.files.exclude": {
"**/.vscode": true, "**/.vscode": true,
"**/.vs": true, "**/.vs": true,
} },
"dimmer.enabled": true
} }

View File

@ -33,7 +33,7 @@ Module build order:
## Milestone ## Milestone
Day 30 : Moving Between Tile Maps Day 33 : Virtualized Tile Maps
Features Done so far: Features Done so far:
@ -61,6 +61,8 @@ Features Done so far:
## Gallery ## Gallery
![img](docs/imgs/handmade_win32_2023-10-11_00-47-19.gif)
![img](docs/imgs/handmade_win32_2023-10-10_13-13-14.gif)
![img](docs/imgs/handmade_win32_2023-10-06_12-43-47.gif) ![img](docs/imgs/handmade_win32_2023-10-06_12-43-47.gif)
![img](docs/imgs/handmade_win32_2023-10-01_20-22-20.gif) ![img](docs/imgs/handmade_win32_2023-10-01_20-22-20.gif)
![img](docs/imgs/Code_2023-10-01_19-20-56.gif) ![img](docs/imgs/Code_2023-10-01_19-20-56.gif)

298
docs/Day 001-025.md Normal file
View File

@ -0,0 +1,298 @@
# Days 1-25
Initial setup and platform layer bootstrapping.
## Day 1
Project initially setup. Followed the video and the book.
I went extra since I already have a perferred setup I've recently grown accoustmed to and I'm going to take advantage of the metaprogramming library I've recently made for myself. Although I doubt it'll see much action...
I'll be keeping the code pretty C like as Casey does in the series. Main purpose is to be able to use whitebox.
## Day 2
Pretty smooth so far, modular header library is showing its limitations...
I have enough practice messsing with forwards and library linkage now that it wasn't much of a show stopper at least. All the mess is quarantined in the in win32.h and so far it looks like it will be enough to keep it all there.
I added extra stuff in `grime.h`, `macros.h`, and `types.h` that he will either go over soon from my vauge memory last time I just watched him (instead of doing) + stuff from gencpp's version of the zpl library.
![img](https://files.catbox.moe/wxasgz.png)
## Day 3
Was able to follow along just fine, lots of more `Gdi32`` stuff to forward in `win32.h`.
## Day 11
Architecture discussion was great. Did some diagramming of it.
I'm going to use a 3 layer approach to the architecture:
* Platform
* Engine
* Game (Handmade)
It will be style 2 so they each will provide a service to the layer below it and platform will provide an API to both engine and game.
## Day 12
This day was a mess to follow along.
> Always write the usage code first if you can, when designing something like an API.
Casey decided to do some heavy hits the audio (makes sense) due to the use of DirectSound.
I'm going to preserve his implmentation as is for the most part since I rather keep things simple so that this project actually gets done.
> Only introduce added complexity when you need it.
There are two reasons you allow for a more complex interface, you ***NEED*** the feature. It actually is required for the project to have for the UX to preserve the user-level features. Or, there is a performance bottleneck that you need to address.
## Day 13
Started to lifting input, not happy about my abstraction to gamepad state in hindsight. Which is kinda lead by how Casey is doing it.
The reality of how input is actually done in the games; that I've worked on; is we don't abstract the gamepad.
We just make a layout per device we decide to support (Dualsense/shock, xinput, nintendo, etc) and then just see what device is mapped to what player. This allows us to have a tailored layout to the device. Doing otherwise usually leads to some discrepancy in the UX for that layout for the user.
Casey has no concept of a player nor a device beyond xinput for this engine (at least up to this point), which is most likely fine for the scope of this project.
For the purposes of my version of Handmade I will make 3 devices that could be mapped to a `Controller` struct: `Keyboard & Mouse`, `Dualsense`, and `XInput`.
This is to retain implementation Casey is already making while also natively supporting the Dualsense controller from sony which I personally perfer to use.
From the 3 layer abstraction (platform/engine/game):
Platform will deal with the raw polling (and most likely down the line have a thread dedicated to running it as fast as possible).
It will provide the engine layer the pad states in the following struct:
```cpp
struct InputState
{
ControllerState Controllers[4];
};
```
There can only be four controllers available at a time and are equivalnet to the input aspect of a local player in Unreal.
(Sort of)
Each controller can be assigned the following:
```cpp
struct ControllerState
{
KeyboardState* Keyboard;
MousesState* Mouse;
XInputPadState* XPad;
DualsensePadState* DSPad;
};
```
If any are null that means the controller has no assignment.
Each pad state is the latest state but the platform keeps a record of the previous frame's state to compare against.
Exmaple for Dualsense:
```cpp
struct DualsensePadState
{
struct
{
AnalogStick Left;
AnalogStick Right;
} Stick;
AnalogAxis L2;
AnalogAxis R2;
union {
DigitalBtn Btns[14];
struct {
struct {
DigitalBtn Up;
DigitalBtn Down;
DigitalBtn Left;
DigitalBtn Right;
} DPad;
DigitalBtn X;
DigitalBtn Circle;
DigitalBtn Square;
DigitalBtn Triangle;
DigitalBtn Share;
DigitalBtn Options;
DigitalBtn L1;
DigitalBtn R1;
};
};
b32 using_analog()
{
return true;
};
};
```
The game layer handles mapping an input state of a controller to an action binding.
Possible action binding when we get to that point:
```cpp
struct ActionBinding
{
String InternalName;
StringCached LocalizedName;
DigitalBtnBinding* BtnBinds;
AxisBinding* AxisBinds;
StickBinding* StickBinds;
MouseBinding* MouseBinds;
};
```
## Day 14
The `game_state` example felt like a forced way to start doing memory management but thats fine.
I'm not making any design changes to it other than setting it as engine::Memory instead of game_memory (since I'm doing 3 api layers instead of the 2 hes doing).
Learning about the virtual memory mapping was nice, it was something on a backburner for me to learn about but I never got around to it.
The address setting was quite awesome to see.
### Some thoughts on platform API
I like Casey's one call idea he emphasized in the Q&A, don't really want to detract from that.
Instead I just want to change it so that by the time you enter the engine loop you are guaranteed to have all the engine data provided by the platform layer.
But, without having to shove everything in the procedure captures. Instead, just have an global engine context thats invalid until engine initialization is done by the platfomr layer.
So it might end up something like this:
![img](https://files.catbox.moe/u1q1au.png)
It ends up making two api paths, one for engine to do initialization when platform provides all the critical data engine needs, and another where the platform provides the engine update function.
It will be known to the user and engine that platform will update some of the engine data ( like input, render, sound, etc ), which will be done in the platform update function.
This is already the case, just need to convey it more strongly if I'm decoupling it from the procedure captures.
This is so that I don't have to do insane chaining to pass these captures when I know some of this data should be contextually known to all procedures within the engine.
I have a feeling Casey may already aleivate this or show that it's not a problem in the future so we'll see and I'll adjust accordingly.
I premtively also did the boxes for an 'editor' and 'game' api. Where there could be possible `n` amount of api layers. I'm not sure if I'll do this, but I'm leaving it open for now. What will most likely happen is they'll still be priority to keep it to a very minimal amount, and not give the game or editor the ability to poke at the engine much at all (which leads to the circular API Casey is warning about).
## Day 15
Casey decides to restrict File IO to 32 bit limits for file size (under 4 gigs). This is due to a limitation do the win32 API in how much it can read into a provided buffer to `ReadFile` by the size of DWORD (4-bytes). For the purposes of this engine and problably almost all applications this is alright.
IF somehow more is needed, just reading in chunks of 4 gigs at a time is fine.
I ended up watching a vod earlier recommended: Mysteries of Memory Management Revealed,with Mark Russinovich
[Part 1](https://www.youtube.com/watch?v=TrFEgHr72Yg&t)
[Part 2](https://www.youtube.com/watch?v=RsQyc4xiJeo)
Was really nice, I got some of the equivalent info for linux.
## Day 16
Finding out about the linker's map file was nice.
## Day 17
Talks about functional programming. Mostly an emphasis of not abusing global state fields or constricting the interface a state may be musted from. With the 'ideal' if performance is not heavily impacted is to use a pure function.
Like I said in Day 14 however I do want to setup a less stack-spam way to pass around data for ergonomics, so instead I'll be using static analysis with gencpp to make sure they are only used in the correct places (there will be a list of function names that are allowed to use the global state, and if it is used in any other function it will throw an error, preventing handmade from compiling).
I wish Casey used the `GetAsyncKeyState` instead of this window messaging.
(I'll be using it, not a fan of getting input callbacks from the window)
I didn't do iteration accross all controllers, I'll worry about that when I setup multiple players.
When it comes to the "controller stick averaging" and abstracting that to actions (like he did) hes bleeding into what should really be an input system with bounded actions, but he doesn't want to make an input system (so its just rapid hardcoding).
I'll do the averaging on the platform layer (for now) since it simple, but like said previously, I will not be allowing the engine layer to abstract what API devices are assigned to a "controller".
## Day 19
I started to do some cleanup while also following along.
`handmade_win32.cpp` now just holds the cpp build order when running the build script for unity builds.
I want to eventually support segemnted builds as well, that way I can make sure there isn't some unhandled usaged of a global or some other symbol in one of the source files.
Evetually I can also use gencpp perform some static analysis to make sure differnt layers are not accessing symbols that they should not.
I started to setup also pulling the polling of input out to a different function.
There are a few routes to go about it:
* Make a separate function per input type: ( poll_keyboard, poll_xinput, poll_jsl ).
* Make a single function that handles all input polling (poll_input or process_input).
* (problably more...)
I'm gravitating toward the second option, the amount of content is not that large, and would keep the code all together along with its control flow.
Overall this was pretty brutual to follow along with for the vod. Thankfully handmade-notes exists..
Reading the end of the notes page for the episode showed a forum link to mmozeiko discussing use of the Windows Core Audio API to alleviate the large amount of latency required to operate with DirectSound.
I'm going to wait until after day 20 since Casey has that one titled "Debugging the Aduio Sync"
## Day 20
This stream was easier to follow than the last. I'm starting to gain some intuition on his style.
### Archtecture wise
I'm deciding to cause some more complexity leakage to the API layers..
I'll restrict it to on-demand but I want to have the engine have configurable options modern games tend to have, if I find it fullfilling todo so I may also expose some configuration for aspects of the engine, most games do not allow the user to mess with.
This was something I wanted to do with my previous attempt and constructing a "probeable" game engine, however, that attempt failed because I kept the API granularity far to high resolution.. and it was *Modern C++* which is the project I learned the hardway was not worth the mental effort to maintain or engage with.
(I also dove straight into graphics programming with Vulkan... making my own vulkan wrapper library... At least I learned a good chunk of the driver's graphics pipeline..)
Debug visualization for the audio was great.
## Day 21
So I learned today the good reason why he doesn't use static variables; So that when he makes this library dynamically loaded we don't lose the state of the game on reload.
This day was extremely gratifying to get working.
I went the extra mile than what Casey did and allow for name mangled symbols (for both clang and msvc).
It took a few more steps than his solution but I get to keep the eronomics of namespaces.
After the linker finishes emitting, I use the build script to parse the .map file and extract the decorated symbols I need to load in the platform layer. Those are exported to a file called `handmade_engine.symbols` and then in the platform layer I load them up using an enum as the lookup table for the line the symbol will be in the file. From there its just loading up the symbol with GetProcAddress!
![img](https://files.catbox.moe/sdebtc.png)
![img](https://files.catbox.moe/ufkacl.png)
![img](https://files.catbox.moe/zr52au.png)
![img](https://files.catbox.moe/g3f1vv.png)
## Day 22
There is an issue with the hot-reload on vscode, does not show up in vs2022 debugger nor remedybg so I won't bother with it.
(Debugging hot-reloaded module doesn't work with vscode's debugger)
## Day 23
Getting state saving was nice.
I decided to expose the file inteface through the platform API permanently, so that I can use it for other things later on.
I'm changing my varaible naming convention in structus from UpperCamel to lower_snake_case.
I realized that semantic highlighting covers any contextual issues along with editor intellisense.
Even at worst case with public/protected/private specifers intellisense covers it.
So I'm going with the option that keeps my naming convention consistent and more flexible to use with Casey's exploratory programming style.
## Day 24
Did lots of cleanup & refactoring on my side.
Started to use gencpp to generate the engne module symbol constants for the platform layer.
## Day 25
Him doing replay save states at the platform layer is causing issues for me...
I'm going to most likely end up doing two categories of save states, one for engine level and one for platform level.
The engine level can just store it in the memory struct, and the game memory can just stay where it is.
I want to use the same "save state" slots for both engine and game since there is no need different memory buckets for that. They will at worst case be ofcourse the size of engine's memory block.
So if there is 4, we are looking at 5x the actual memory size. This is fine for now since the engine block is 2 gigs.
Snapshots of of memory are are handled using take and load snapshot functions. They can be used separate from the replay looping functionality.
Replay looping however is dependent on snapshots so if a replay is save/loaded it will use the same snapshot slot of memory to do that state management.
Feels great to be past the platform layer bulk!!!

View File

@ -1,7 +0,0 @@
# Day 1
Project initially setup. Followed the video and the book.
I went extra since I already have a perferred setup I've recently grown accoustmed to and I'm going to take advantage of the metaprogramming library I've recently made for myself. Although I doubt it'll see much action...
I'll be keeping the code pretty C like as Casey does in the series. Main purpose is to be able to use whitebox.

View File

@ -1,9 +0,0 @@
# Day 2
Pretty smooth so far, modular header library is showing its limitations...
I have enough practice messsing with forwards and library linkage now that it wasn't much of a show stopper at least. All the mess is quarantined in the in win32.h and so far it looks like it will be enough to keep it all there.
I added extra stuff in `grime.h`, `macros.h`, and `types.h` that he will either go over soon from my vauge memory last time I just watched him (instead of doing) + stuff from gencpp's version of the zpl library.
![img](https://files.catbox.moe/wxasgz.png)

View File

@ -1,4 +0,0 @@
# Day 3
Was able to follow along just fine, lots of more `Gdi32`` stuff to forward in `win32.h`.

View File

@ -1,2 +0,0 @@
# Day 4

View File

@ -1,12 +0,0 @@
# Day 11
Architecture discussion was great. Did some diagramming of it.
I'm going to use a 3 layer approach to the architecture:
* Platform
* Engine
* Game (Handmade)
It will be style 2 so they each will provide a service to the layer below it and platform will provide an API to both engine and game.

View File

@ -1,13 +0,0 @@
# Day 12
This day was a mess to follow along.
> Always write the usage code first if you can, when designing something like an API.
Casey decided to do some heavy hits the audio (makes sense) due to the use of DirectSound.
I'm going to preserve his implmentation as is for the most part since I rather keep things simple so that this project actually gets done.
> Only introduce added complexity when you need it.
There are two reasons you allow for a more complex interface, you ***NEED*** the feature. It actually is required for the project to have for the UX to preserve the user-level features. Or, there is a performance bottleneck that you need to address.

View File

@ -1,96 +0,0 @@
# Day 13
Started to lifting input, not happy about my abstraction to gamepad state in hindsight. Which is kinda lead by how Casey is doing it.
The reality of how input is actually done in the games; that I've worked on; is we don't abstract the gamepad.
We just make a layout per device we decide to support (Dualsense/shock, xinput, nintendo, etc) and then just see what device is mapped to what player. This allows us to have a tailored layout to the device. Doing otherwise usually leads to some discrepancy in the UX for that layout for the user.
Casey has no concept of a player nor a device beyond xinput for this engine (at least up to this point), which is most likely fine for the scope of this project.
For the purposes of my version of Handmade I will make 3 devices that could be mapped to a `Controller` struct: `Keyboard & Mouse`, `Dualsense`, and `XInput`.
This is to retain implementation Casey is already making while also natively supporting the Dualsense controller from sony which I personally perfer to use.
From the 3 layer abstraction (platform/engine/game):
Platform will deal with the raw polling (and most likely down the line have a thread dedicated to running it as fast as possible).
It will provide the engine layer the pad states in the following struct:
```cpp
struct InputState
{
ControllerState Controllers[4];
};
```
There can only be four controllers available at a time and are equivalnet to the input aspect of a local player in Unreal.
(Sort of)
Each controller can be assigned the following:
```cpp
struct ControllerState
{
KeyboardState* Keyboard;
MousesState* Mouse;
XInputPadState* XPad;
DualsensePadState* DSPad;
};
```
If any are null that means the controller has no assignment.
Each pad state is the latest state but the platform keeps a record of the previous frame's state to compare against.
Exmaple for Dualsense:
```cpp
struct DualsensePadState
{
struct
{
AnalogStick Left;
AnalogStick Right;
} Stick;
AnalogAxis L2;
AnalogAxis R2;
union {
DigitalBtn Btns[14];
struct {
struct {
DigitalBtn Up;
DigitalBtn Down;
DigitalBtn Left;
DigitalBtn Right;
} DPad;
DigitalBtn X;
DigitalBtn Circle;
DigitalBtn Square;
DigitalBtn Triangle;
DigitalBtn Share;
DigitalBtn Options;
DigitalBtn L1;
DigitalBtn R1;
};
};
b32 using_analog()
{
return true;
};
};
```
The game layer handles mapping an input state of a controller to an action binding.
Possible action binding when we get to that point:
```cpp
struct ActionBinding
{
String InternalName;
StringCached LocalizedName;
DigitalBtnBinding* BtnBinds;
AxisBinding* AxisBinds;
StickBinding* StickBinds;
MouseBinding* MouseBinds;
};
```

View File

@ -1,29 +0,0 @@
# Day 14
The `game_state` example felt like a forced way to start doing memory management but thats fine.
I'm not making any design changes to it other than setting it as engine::Memory instead of game_memory (since I'm doing 3 api layers instead of the 2 hes doing).
Learning about the virtual memory mapping was nice, it was something on a backburner for me to learn about but I never got around to it.
The address setting was quite awesome to see.
## Some thoughts on platform API
I like Casey's one call idea he emphasized in the Q&A, don't really want to detract from that.
Instead I just want to change it so that by the time you enter the engine loop you are guaranteed to have all the engine data provided by the platform layer.
But, without having to shove everything in the procedure captures. Instead, just have an global engine context thats invalid until engine initialization is done by the platfomr layer.
So it might end up something like this:
![img](https://files.catbox.moe/u1q1au.png)
It ends up making two api paths, one for engine to do initialization when platform provides all the critical data engine needs, and another where the platform provides the engine update function.
It will be known to the user and engine that platform will update some of the engine data ( like input, render, sound, etc ), which will be done in the platform update function.
This is already the case, just need to convey it more strongly if I'm decoupling it from the procedure captures.
This is so that I don't have to do insane chaining to pass these captures when I know some of this data should be contextually known to all procedures within the engine.
I have a feeling Casey may already aleivate this or show that it's not a problem in the future so we'll see and I'll adjust accordingly.
I premtively also did the boxes for an 'editor' and 'game' api. Where there could be possible `n` amount of api layers. I'm not sure if I'll do this, but I'm leaving it open for now. What will most likely happen is they'll still be priority to keep it to a very minimal amount, and not give the game or editor the ability to poke at the engine much at all (which leads to the circular API Casey is warning about).

View File

@ -1,11 +0,0 @@
# Day 15
Casey decides to restrict File IO to 32 bit limits for file size (under 4 gigs). This is due to a limitation do the win32 API in how much it can read into a provided buffer to `ReadFile` by the size of DWORD (4-bytes). For the purposes of this engine and problably almost all applications this is alright.
IF somehow more is needed, just reading in chunks of 4 gigs at a time is fine.
I ended up watching a vod earlier recommended: Mysteries of Memory Management Revealed,with Mark Russinovich
[Part 1](https://www.youtube.com/watch?v=TrFEgHr72Yg&t)
[Part 2](https://www.youtube.com/watch?v=RsQyc4xiJeo)
Was really nice, I got some of the equivalent info for linux.

View File

@ -1,3 +0,0 @@
# Day 16
Finding out about the linker's map file was nice.

View File

@ -1,13 +0,0 @@
# Day 17
Talks about functional programming. Mostly an emphasis of not abusing global state fields or constricting the interface a state may be musted from. With the 'ideal' if performance is not heavily impacted is to use a pure function.
Like I said in Day 14 however I do want to setup a less stack-spam way to pass around data for ergonomics, so instead I'll be using static analysis with gencpp to make sure they are only used in the correct places (there will be a list of function names that are allowed to use the global state, and if it is used in any other function it will throw an error, preventing handmade from compiling).
I wish Casey used the `GetAsyncKeyState` instead of this window messaging.
(I'll be using it, not a fan of getting input callbacks from the window)
I didn't do iteration accross all controllers, I'll worry about that when I setup multiple players.
When it comes to the "controller stick averaging" and abstracting that to actions (like he did) hes bleeding into what should really be an input system with bounded actions, but he doesn't want to make an input system (so its just rapid hardcoding).
I'll do the averaging on the platform layer (for now) since it simple, but like said previously, I will not be allowing the engine layer to abstract what API devices are assigned to a "controller".

View File

@ -1,20 +0,0 @@
# Day 19
I started to do some cleanup while also following along.
`handmade_win32.cpp` now just holds the cpp build order when running the build script for unity builds.
I want to eventually support segemnted builds as well, that way I can make sure there isn't some unhandled usaged of a global or some other symbol in one of the source files.
Evetually I can also use gencpp perform some static analysis to make sure differnt layers are not accessing symbols that they should not.
I started to setup also pulling the polling of input out to a different function.
There are a few routes to go about it:
* Make a separate function per input type: ( poll_keyboard, poll_xinput, poll_jsl ).
* Make a single function that handles all input polling (poll_input or process_input).
* (problably more...)
I'm gravitating toward the second option, the amount of content is not that large, and would keep the code all together along with its control flow.
Overall this was pretty brutual to follow along with for the vod. Thankfully handmade-notes exists..
Reading the end of the notes page for the episode showed a forum link to mmozeiko discussing use of the Windows Core Audio API to alleviate the large amount of latency required to operate with DirectSound.
I'm going to wait until after day 20 since Casey has that one titled "Debugging the Aduio Sync"

View File

@ -1,18 +0,0 @@
# Day 20
This stream was easier to follow than the last. I'm starting to gain some intuition on his style.
## Archtecture wise
I'm deciding to cause some more complexity leakage to the API layers..
I'll restrict it to on-demand but I want to have the engine have configurable options modern games tend to have, if I find it fullfilling todo so I may also expose some configuration for aspects of the engine, most games do not allow the user to mess with.
This was something I wanted to do with my previous attempt and constructing a "probeable" game engine, however, that attempt failed because I kept the API granularity far to high resolution.. and it was *Modern C++* which is the project I learned the hardway was not worth the mental effort to maintain or engage with.
(I also dove straight into graphics programming with Vulkan... making my own vulkan wrapper library... At least I learned a good chunk of the driver's graphics pipeline..)
Debug visualization for the audio was great.

View File

@ -1,15 +0,0 @@
# Day 21
So I learned today the good reason why he doesn't use static variables; So that when he makes this library dynamically loaded we don't lose the state of the game on reload.
This day was extremely gratifying to get working.
I went the extra mile than what Casey did and allow for name mangled symbols (for both clang and msvc).
It took a few more steps than his solution but I get to keep the eronomics of namespaces.
After the linker finishes emitting, I use the build script to parse the .map file and extract the decorated symbols I need to load in the platform layer. Those are exported to a file called `handmade_engine.symbols` and then in the platform layer I load them up using an enum as the lookup table for the line the symbol will be in the file. From there its just loading up the symbol with GetProcAddress!
![img](https://files.catbox.moe/sdebtc.png)
![img](https://files.catbox.moe/ufkacl.png)
![img](https://files.catbox.moe/zr52au.png)
![img](https://files.catbox.moe/g3f1vv.png)

View File

@ -1,4 +0,0 @@
# Day 22
There is an issue with the hot-reload on vscode, does not show up in vs2022 debugger nor remedybg so I won't bother with it.
(Debugging hot-reloaded module doesn't work with vscode's debugger)

View File

@ -1,11 +0,0 @@
# Day 23
Getting state saving was nice.
I decided to expose the file inteface through the platform API permanently, so that I can use it for other things later on.
I'm changing my varaible naming convention in structus from UpperCamel to lower_snake_case.
I realized that semantic highlighting covers any contextual issues along with editor intellisense.
Even at worst case with public/protected/private specifers intellisense covers it.
So I'm going with the option that keeps my naming convention consistent and more flexible to use with Casey's exploratory programming style.

View File

@ -1,5 +0,0 @@
# Day 24
Did lots of cleanup & refactoring on my side.
Started to use gencpp to generate the engne module symbol constants for the platform layer.

View File

@ -2,4 +2,4 @@
Vod was more of a pain to follow along than usual... Vod was more of a pain to follow along than usual...
I updated the scripts to only bild each module if it detects changes (just using md5 hash on the files). I updated the scripts to only build each module if it detects changes (just using md5 hash on the files).

3
docs/Day 033.md Normal file
View File

@ -0,0 +1,3 @@
# Day 033
I thought the last few vods were not great to follow... That was just training for today.

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

View File

@ -60,195 +60,16 @@ struct EngineState
}; };
NS_ENGINE_END NS_ENGINE_END
#include "test_samples.cpp" #include "test_samples.cpp"
NS_ENGINE_BEGIN
#if Build_Development #if Build_Development
using SnapshotFn = void ( Memory* memory, platform::ModuleAPI* platform_api ); #include "state_and_replay.cpp"
internal
void load_engine_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
{
platform_api->memory_copy( memory->persistent, memory->total_size()
, memory->snapshots[ memory->active_snapshot_slot ].memory );
}
internal
void load_game_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
{
s32 slot = memory->active_snapshot_slot;
EngineState* state = rcast( EngineState*, memory->persistent );
void* persistent_slot = memory->snapshots[ slot ].memory;
void* transient_slot = rcast( Byte*, memory->snapshots[ slot ].memory ) + state->game_memory.persistent_size;
platform_api->memory_copy( state->game_memory.persistent, state->game_memory.persistent_size, persistent_slot );
platform_api->memory_copy( state->game_memory.transient, state->game_memory.transient_size, transient_slot );
}
internal
void take_engine_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
{
platform_api->memory_copy( memory->snapshots[ memory->active_snapshot_slot ].memory, memory->total_size(), memory->persistent );
memory->snapshots[ memory->active_snapshot_slot ].age = platform_api->get_wall_clock();
}
internal
void take_game_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
{
s32 slot = memory->active_snapshot_slot;
EngineState* state = rcast( EngineState*, memory->persistent );
void* persistent_slot = memory->snapshots[ slot ].memory;
void* transient_slot = rcast( Byte*, memory->snapshots[ slot ].memory ) + state->game_memory.persistent_size;
platform_api->memory_copy( persistent_slot, state->game_memory.persistent_size, state->game_memory.persistent );
platform_api->memory_copy( transient_slot, state->game_memory.transient_size, state->game_memory.transient );
memory->snapshots[ memory->active_snapshot_slot ].age = platform_api->get_wall_clock();
}
internal
void begin_recording_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
Str file_name = str_ascii("test_input_");
StrPath file_path = {};
file_path.concat( platform_api->path_scratch, file_name );
snprintf( file_path.ptr, file_path.len, "%s%d.hm_replay", file_name.ptr, memory->active_snapshot_slot );
memory->active_input_replay_file.path = file_path;
platform_api->file_delete( memory->active_input_replay_file.path );
memory->replay_mode = ReplayMode_Record;
}
internal
void end_recording_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
memory->replay_mode = ReplayMode_Off;
platform_api->file_close( & memory->active_input_replay_file );
}
internal
void begin_playback_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
Str file_name = str_ascii("test_input_");
StrPath file_path = {};
file_path.concat( platform_api->path_scratch, file_name );
snprintf( file_path.ptr, file_path.len, "%s%d.hm_replay", file_name.ptr, memory->active_snapshot_slot );
// TODO(Ed - From Casey): Recording system still seems to take too long
// on record start - find out what Windows is doing and if
// we can speed up / defer some of that processing.
if ( platform_api->file_check_exists( file_path ) )
{
memory->active_input_replay_file.path = file_path;
memory->replay_mode = ReplayMode_Playback;
}
else
{
// TODO(Ed) : Logging
}
}
internal
void end_playback_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
memory->replay_mode = ReplayMode_Off;
platform_api->file_close( & memory->active_input_replay_file );
}
InputStateSnapshot input_state_snapshot( InputState* input )
{
InputStateSnapshot snapshot = {};
for ( s32 idx = 0; idx < array_count( snapshot.controllers ); ++ idx )
{
ControllerState* controller = & input->controllers[idx];
if ( controller == nullptr )
continue;
if ( controller->ds_pad )
snapshot.controllers[idx].ds_pad = *controller->ds_pad;
if ( controller->xpad )
snapshot.controllers[idx].xpad = *controller->xpad;
if ( controller->keyboard )
{
snapshot.controllers[idx].keyboard = *controller->keyboard;
}
if ( controller->mouse )
snapshot.controllers[idx].mouse = *controller->mouse;
}
return snapshot;
}
internal
void record_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
InputStateSnapshot snapshot = input_state_snapshot( input );
if ( platform_api->file_write_stream( & memory->active_input_replay_file, sizeof(snapshot), &snapshot ) == 0 )
{
// TODO(Ed) : Logging
}
}
internal
void play_input( SnapshotFn* load_snapshot, Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
InputStateSnapshot new_input;
if ( platform_api->file_read_stream( & memory->active_input_replay_file, sizeof(InputStateSnapshot), & new_input ) == 0 )
{
load_snapshot( memory, platform_api );
platform_api->file_rewind( & memory->active_input_replay_file );
return;
}
for ( s32 idx = 0; idx < array_count( new_input.controllers ); ++ idx )
{
ControllerState* controller = & input->controllers[idx];
if ( controller == nullptr )
continue;
if ( controller->ds_pad )
*controller->ds_pad = new_input.controllers[idx].ds_pad;
if ( controller->xpad )
*controller->xpad = new_input.controllers[idx].xpad;
if ( controller->keyboard )
{
*controller->keyboard = new_input.controllers[idx].keyboard;
}
if ( controller->mouse )
*controller->mouse = new_input.controllers[idx].mouse;
}
}
void process_loop_mode( SnapshotFn* take_snapshot, SnapshotFn* load_snapshot
, Memory* memory, EngineState* state, InputState* input, platform::ModuleAPI* platform_api )
{
if ( memory->replay_mode == ReplayMode_Off )
{
take_snapshot( memory, platform_api );
begin_recording_input( memory, input, platform_api );
}
else if ( memory->replay_mode == ReplayMode_Playback )
{
end_playback_input( memory, input, platform_api );
load_snapshot( memory, platform_api );
}
else if ( memory->replay_mode == ReplayMode_Record )
{
end_recording_input( memory, input, platform_api );
load_snapshot( memory, platform_api );
begin_playback_input( memory, input, platform_api );
}
}
// Build_Development // Build_Development
#endif #endif
NS_ENGINE_BEGIN
internal internal
void input_poll_engine_actions( InputState* input, EngineActions* actions ) void input_poll_engine_actions( InputState* input, EngineActions* actions )
{ {
@ -357,10 +178,10 @@ void draw_rectangle( OffscreenBuffer* buffer
, f32 max_x, f32 max_y , f32 max_x, f32 max_y
, f32 red, f32 green, f32 blue ) , f32 red, f32 green, f32 blue )
{ {
s32 min_x_32 = round_f32_to_s32( min_x ); s32 min_x_32 = round( min_x );
s32 min_y_32 = round_f32_to_s32( min_y ); s32 min_y_32 = round( min_y );
s32 max_x_32 = round_f32_to_s32( max_x ); s32 max_x_32 = round( max_x );
s32 max_y_32 = round_f32_to_s32( max_y ); s32 max_y_32 = round( max_y );
s32 buffer_width = buffer->width; s32 buffer_width = buffer->width;
s32 buffer_height = buffer->height; s32 buffer_height = buffer->height;
@ -374,9 +195,9 @@ void draw_rectangle( OffscreenBuffer* buffer
if ( max_y_32 > buffer_height ) if ( max_y_32 > buffer_height )
max_y_32 = buffer_height; max_y_32 = buffer_height;
s32 red_32 = round_f32_to_s32( 255.f * red ); s32 red_32 = round( 255.f * red );
s32 green_32 = round_f32_to_s32( 255.f * green ); s32 green_32 = round( 255.f * green );
s32 blue_32 = round_f32_to_s32( 255.f * blue ); s32 blue_32 = round( 255.f * blue );
s32 color = s32 color =
(scast(s32, red_32) << 16) (scast(s32, red_32) << 16)
@ -401,6 +222,125 @@ void draw_rectangle( OffscreenBuffer* buffer
} }
} }
inline
void cannonicalize_coord( World* world, u32* tile_coord, f32* pos_coord )
{
f32 tile_size = scast(f32, world->tile_size_in_meters);
// Note(Ed) : World is assumed to be a "torodial topology"
s32 offset = floor( (* pos_coord) / tile_size );
u32 new_tile_coord = (* tile_coord) + offset;
f32 new_pos_coord = (* pos_coord) - scast(f32, offset) * tile_size;
assert( new_pos_coord >= 0 );
assert( new_pos_coord <= tile_size );
(* tile_coord) = new_tile_coord;
(* pos_coord) = new_pos_coord;
}
inline
WorldPosition recannonicalize_position( World* world, WorldPosition pos )
{
WorldPosition result = pos;
cannonicalize_coord( world, & result.tile_x, & result.x );
cannonicalize_coord( world, & result.tile_y, & result.y );
return result;
}
inline
u32 TileChunk_tile_value( TileChunk* tile_chunk, World* world, u32 x, u32 y )
{
assert( world != nullptr );
assert( tile_chunk != nullptr );
assert( x < world->chunk_dimension );
assert( y < world->chunk_dimension );
u32 value = tile_chunk->tiles[ (y * world->chunk_dimension) + x ];
return value;
}
// TODO(Ed) : Review if this is needed anymore?
#if 0
inline
b32 TileChunk_is_tile_empty( TileChunk* tile_chunk, World* world, u32 tile_x, u32 tile_y )
{
//assert( tile_chunk != nullptr );
assert( world != nullptr );
// Assume space is occupied if there is bad data
if ( tile_chunk == nullptr )
return false;
b32 is_empty = false;
u32 tile_value = TileChunk_tile_value( tile_chunk, world, tile_x, tile_y );
is_empty = tile_value == 0;
return is_empty;
}
#endif
inline
TileChunk* World_get_tile_chunk( World* world, s32 tile_chunk_x, s32 tile_chunk_y )
{
TileChunk* chunk = nullptr;
if ( tile_chunk_x >= 0 && tile_chunk_x < scast(s32, world->tile_chunks_num_x)
&& tile_chunk_y >= 0 && tile_chunk_y < scast(s32, world->tile_chunks_num_y) )
{
chunk = & world->tile_chunks[ tile_chunk_y * world->tile_chunks_num_x + tile_chunk_x ];
}
return chunk;
}
inline
TileChunkPosition get_tile_chunk_position_for( World* world, u32 abs_tile_x, u32 abs_tile_y )
{
TileChunkPosition chunk_pos {};
chunk_pos.tile_chunk_x = abs_tile_x >> world->chunk_shift;
chunk_pos.tile_chunk_y = abs_tile_y >> world->chunk_shift;
chunk_pos.tile_x = abs_tile_x & world->chunk_mask;
chunk_pos.tile_y = abs_tile_y & world->chunk_mask;
return chunk_pos;
}
internal // TODO(Ed) : Review if he keeps this crap.
u32 piggy_get_tile_value( World* world, u32 tile_x, u32 tile_y )
{
assert( world != nullptr );
u32 value = 0;
TileChunkPosition chunk_pos = get_tile_chunk_position_for( world, tile_x, tile_y );
TileChunk* chunk = World_get_tile_chunk( world, chunk_pos.tile_chunk_x, chunk_pos.tile_chunk_y );
if ( chunk )
value = TileChunk_tile_value( chunk, world, chunk_pos.tile_x, chunk_pos.tile_y );
return value;
}
internal
b32 world_is_point_empty( World* world, WorldPosition position )
{
assert( world != nullptr );
u32 chunk_value = piggy_get_tile_value( world, position.tile_x, position.tile_y );
b32 is_empty = chunk_value == 0;
return is_empty;
}
inline
void draw_debug_point(OffscreenBuffer* back_buffer, World* world, WorldPosition pos, f32 red, f32 green, f32 blue)
{
draw_rectangle(back_buffer,
pos.x * world->tile_meters_to_pixels + world->tile_lower_left_x + scast(f32, pos.tile_x * world->tile_size_in_pixels),
pos.y * world->tile_meters_to_pixels + world->tile_lower_left_y + scast(f32, pos.tile_y * world->tile_size_in_pixels),
(pos.x + 0.1f) * world->tile_meters_to_pixels + world->tile_lower_left_x + scast(f32, pos.tile_x * world->tile_size_in_pixels),
(pos.y + 0.1f) * world->tile_meters_to_pixels + world->tile_lower_left_y + scast(f32, pos.tile_y * world->tile_size_in_pixels),
red, green, blue);
}
Engine_API Engine_API
void on_module_reload( Memory* memory, platform::ModuleAPI* platfom_api ) void on_module_reload( Memory* memory, platform::ModuleAPI* platfom_api )
{ {
@ -449,8 +389,6 @@ void startup( Memory* memory, platform::ModuleAPI* platform_api )
assert( sizeof(hh::GameState) <= state->game_memory.persistent_size ); assert( sizeof(hh::GameState) <= state->game_memory.persistent_size );
hh::PlayerState* player = & game_state->player_state; hh::PlayerState* player = & game_state->player_state;
player->position.tile_map_x = 0;
player->position.tile_map_y = 0;
player->position.tile_x = 3; player->position.tile_x = 3;
player->position.tile_y = 3; player->position.tile_y = 3;
player->position.x = 0.f; player->position.x = 0.f;
@ -465,107 +403,6 @@ void shutdown( Memory* memory, platform::ModuleAPI* platform_api )
{ {
} }
inline
void cannonicalize_coord( World* world, s32 num_tiles, s32* tile_map_coord, s32* tile_coord, f32* pos_coord )
{
s32 new_tile_map_coord = *tile_map_coord;
f32 tile_size = scast(f32, world->tile_size_in_meters);
// TODO(Ed) : Need to use an alt method for reconnonicalizing because this can end up rounding back up to the tile.
// TODO(Ed) : Add bounds checking to prevent wrapping
s32 offset = floor_f32_to_s32( (* pos_coord) / tile_size );
s32 new_tile_coord = (* tile_coord) + offset;
f32 new_pos_coord = (* pos_coord) - scast(f32, offset) * tile_size;
assert( new_pos_coord >= 0.f );
assert( new_pos_coord < tile_size );
if ( new_tile_coord < 0 )
{
new_tile_coord += num_tiles;
-- new_tile_map_coord;
}
if ( new_tile_coord >= num_tiles )
{
new_tile_coord -= num_tiles;
++ new_tile_map_coord;
}
(* tile_map_coord) = new_tile_map_coord;
(* tile_coord) = new_tile_coord;
(* pos_coord) = new_pos_coord;
}
inline
CanonPosition recannonicalize_position( World* world, CanonPosition pos )
{
CanonPosition result = pos;
cannonicalize_coord( world, world->num_tiles_x, & result.tile_map_x, & result.tile_x, & result.x );
cannonicalize_coord( world, world->num_tiles_y, & result.tile_map_y, & result.tile_y, & result.y );
return result;
}
inline
u32 tilemap_tile_value( TileMap* tile_map, World* world, s32 x, s32 y )
{
assert( tile_map != nullptr );
assert( world != nullptr );
assert( x >= 0 && x < scast(s32, world->num_tiles_x) );
assert( y >= 0 && y < scast(s32, world->num_tiles_y) );
return tile_map->tiles[ (y * world->num_tiles_x) + x ];
}
inline
b32 tilemap_is_point_empty( TileMap* tile_map, World* world, s32 tile_x, s32 tile_y )
{
assert( tile_map != nullptr );
assert( world != nullptr );
// Assume space is occupied if there is bad data
if ( tile_map == nullptr )
return false;
b32 is_empty = false;
if ( tile_x >= 0 && tile_x < world->num_tiles_x
&& tile_y >= 0 && tile_y < world->num_tiles_y )
{
u32 tile_value = tilemap_tile_value( tile_map, world, tile_x, tile_y );
is_empty = tile_value == 0;
}
return is_empty;
}
inline
TileMap* world_get_tilemap( World* world, s32 tile_map_x, s32 tile_map_y )
{
assert( tile_map_x >= 0 && tile_map_x < world->tilemaps_num_x );
assert( tile_map_y >= 0 && tile_map_y < world->tilemaps_num_y );
return & world->tile_maps[ (tile_map_y * world->tilemaps_num_x) + tile_map_x ];
}
internal
b32 world_is_point_empty( World* world, CanonPosition position )
{
assert( world != nullptr );
b32 is_empty = false;
TileMap* tile_map = world_get_tilemap( world, position.tile_map_x, position.tile_map_y );
is_empty = tilemap_is_point_empty( tile_map, world, position.tile_x, position.tile_y );
return is_empty;
}
void draw_debug_point(OffscreenBuffer* back_buffer, World* world, CanonPosition pos, f32 red, f32 green, f32 blue)
{
draw_rectangle(back_buffer,
pos.x * world->tile_meters_to_pixels + world->tile_upper_left_x + scast(f32, pos.tile_x * world->tile_size_in_pixels),
pos.y * world->tile_meters_to_pixels + world->tile_upper_left_y + scast(f32, pos.tile_y * world->tile_size_in_pixels),
(pos.x + 0.1f) * world->tile_meters_to_pixels + world->tile_upper_left_x + scast(f32, pos.tile_x * world->tile_size_in_pixels),
(pos.y + 0.1f) * world->tile_meters_to_pixels + world->tile_upper_left_y + scast(f32, pos.tile_y * world->tile_size_in_pixels),
red, green, blue);
}
Engine_API Engine_API
void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back_buffer void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back_buffer
, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread ) , Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread )
@ -712,83 +549,53 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
f32 x_offset_f = scast(f32, state->x_offset); f32 x_offset_f = scast(f32, state->x_offset);
f32 y_offset_f = scast(f32, state->y_offset); f32 y_offset_f = scast(f32, state->y_offset);
constexpr s32 tile_map_num_x = 16; constexpr s32 tile_map_num_x = 256;
constexpr s32 tile_map_num_y = 9; constexpr s32 tile_map_num_y = 256;
// tiles_XY // tiles_XY
u32 tiles_00 [tile_map_num_y][tile_map_num_x] = { u32 temp_tiles [tile_map_num_y][tile_map_num_x] = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
}; };
u32 tiles_10 [tile_map_num_y][tile_map_num_x] = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
};
u32 tiles_01 [tile_map_num_y][tile_map_num_x] = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
};
u32 tiles_11 [tile_map_num_y][tile_map_num_x] = {
{ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
};
TileMap tile_maps[2][2] {};
tile_maps[0][0].tiles = rcast(u32*, tiles_00);
tile_maps[0][1].tiles = rcast(u32*, tiles_10);
tile_maps[1][0].tiles = rcast(u32*, tiles_01);
tile_maps[1][1].tiles = rcast(u32*, tiles_11);
World world; World world;
world.tile_size_in_meters = 1.4f; world.chunk_shift = 8;
world.tile_size_in_pixels = 85; world.chunk_mask = (1 << world.chunk_shift) - 1;
world.chunk_dimension = 256;
world.tile_chunks_num_x = 1;
world.tile_chunks_num_y = 1;
TileChunk temp_chunk;
temp_chunk.tiles = rcast( u32*, & temp_tiles );
world.tile_chunks = & temp_chunk;
world.tile_size_in_meters = 1.4f;
world.tile_size_in_pixels = 85;
world.tile_meters_to_pixels = scast(f32, world.tile_size_in_pixels) / world.tile_size_in_meters; world.tile_meters_to_pixels = scast(f32, world.tile_size_in_pixels) / world.tile_size_in_meters;
f32 tile_size_in_pixels = scast(f32, world.tile_size_in_pixels); f32 tile_size_in_pixels = scast(f32, world.tile_size_in_pixels);
world.num_tiles_x = tile_map_num_x;
world.num_tiles_y = tile_map_num_y;
f32 scale = 85; f32 scale = 85;
world.tile_upper_left_x = -( tile_size_in_pixels * 0.5f); world.tile_lower_left_x = -( tile_size_in_pixels * 0.5f);
world.tile_upper_left_y = -( tile_size_in_pixels * 0.25f); world.tile_lower_left_y = +( tile_size_in_pixels * 0.25f) + scast(f32, back_buffer->height);
world.tilemaps_num_x = 2;
world.tilemaps_num_y = 2;
world.tile_maps = rcast(TileMap*, tile_maps);
TileMap* current_tile_map = world_get_tilemap( & world, player->position.tile_map_x, player->position.tile_map_y );
assert( current_tile_map != nullptr );
player->height = 1.4f; player->height = 1.4f;
player->width = player->height * 0.7f; player->width = player->height * 0.7f;
@ -798,82 +605,70 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
input_poll_player_actions( input, & player_actions ); input_poll_player_actions( input, & player_actions );
{ {
f32 move_speed = 6.f; f32 move_speed = 4.f;
f32 new_player_pos_x = player->position.x; f32 new_player_pos_x = player->position.x;
f32 new_player_pos_y = player->position.y; f32 new_player_pos_y = player->position.y;
if ( player_actions.player_x_move_analog || player_actions.player_y_move_analog ) if ( player_actions.player_x_move_analog || player_actions.player_y_move_analog )
{ {
new_player_pos_x += scast(f32, player_actions.player_x_move_analog * delta_time * move_speed); new_player_pos_x += scast(f32, player_actions.player_x_move_analog * delta_time * move_speed);
new_player_pos_y -= scast(f32, player_actions.player_y_move_analog * delta_time * move_speed); new_player_pos_y += scast(f32, player_actions.player_y_move_analog * delta_time * move_speed);
} }
else else
{ {
new_player_pos_x += scast(f32, player_actions.player_x_move_digital) * delta_time * move_speed; new_player_pos_x += scast(f32, player_actions.player_x_move_digital) * delta_time * move_speed;
new_player_pos_y -= scast(f32, player_actions.player_y_move_digital) * delta_time * move_speed; new_player_pos_y += scast(f32, player_actions.player_y_move_digital) * delta_time * move_speed;
} }
new_player_pos_y += sinf( player->jump_time * TAU ) * 10.f * delta_time; new_player_pos_y += sinf( player->jump_time * TAU ) * 10.f * delta_time;
b32 valid_new_pos = true; b32 valid_new_pos = true;
{ {
//RawPosition test_pos = { WorldPosition test_pos = {
// new_player_pos_x - player_half_width, new_player_pos_y - player_quarter_height,
// player->position.tile_map_x, player->position.tile_map_y
//};
CanonPosition test_pos = {
new_player_pos_x, new_player_pos_y, new_player_pos_x, new_player_pos_y,
player->position.tile_map_x, player->position.tile_map_y,
player->position.tile_x, player->position.tile_y player->position.tile_x, player->position.tile_y
}; };
test_pos = recannonicalize_position( & world, test_pos ); test_pos = recannonicalize_position( & world, test_pos );
// TODO(Ed) : Need a delta-function that auto-reconnonicalizes. // TODO(Ed) : Need a delta-function that auto-reconnonicalizes.
CanonPosition test_pos_nw { WorldPosition test_pos_nw {
new_player_pos_x - player_half_width, new_player_pos_y - player_quarter_height, new_player_pos_x - player_half_width, new_player_pos_y - player_quarter_height,
player->position.tile_map_x, player->position.tile_map_y,
player->position.tile_x, player->position.tile_y player->position.tile_x, player->position.tile_y
}; };
test_pos_nw = recannonicalize_position( & world, test_pos_nw ); test_pos_nw = recannonicalize_position( & world, test_pos_nw );
valid_new_pos &= world_is_point_empty( & world, test_pos_nw ); valid_new_pos &= world_is_point_empty( & world, test_pos_nw );
CanonPosition test_pos_ne { WorldPosition test_pos_ne {
new_player_pos_x + player_half_width, new_player_pos_y - player_quarter_height, new_player_pos_x + player_half_width, new_player_pos_y - player_quarter_height,
player->position.tile_map_x, player->position.tile_map_y,
player->position.tile_x, player->position.tile_y player->position.tile_x, player->position.tile_y
}; };
test_pos_ne = recannonicalize_position( & world, test_pos_ne ); test_pos_ne = recannonicalize_position( & world, test_pos_ne );
valid_new_pos &= world_is_point_empty( & world, test_pos_ne ); valid_new_pos &= world_is_point_empty( & world, test_pos_ne );
CanonPosition test_pos_sw { WorldPosition test_pos_sw {
new_player_pos_x - player_half_width, new_player_pos_y, new_player_pos_x - player_half_width, new_player_pos_y,
player->position.tile_map_x, player->position.tile_map_y,
player->position.tile_x, player->position.tile_y player->position.tile_x, player->position.tile_y
}; };
test_pos_sw = recannonicalize_position( & world, test_pos_sw ); test_pos_sw = recannonicalize_position( & world, test_pos_sw );
valid_new_pos &= world_is_point_empty( & world, test_pos_sw ); valid_new_pos &= world_is_point_empty( & world, test_pos_sw );
CanonPosition test_pos_se { WorldPosition test_pos_se {
new_player_pos_x + player_half_width, new_player_pos_y, new_player_pos_x + player_half_width, new_player_pos_y,
player->position.tile_map_x, player->position.tile_map_y,
player->position.tile_x, player->position.tile_y player->position.tile_x, player->position.tile_y
}; };
test_pos_se = recannonicalize_position( & world, test_pos_se ); test_pos_se = recannonicalize_position( & world, test_pos_se );
valid_new_pos &= world_is_point_empty( & world, test_pos_se ); valid_new_pos &= world_is_point_empty( & world, test_pos_se );
} }
if ( valid_new_pos ) if ( valid_new_pos )
{ {
CanonPosition new_pos = { WorldPosition new_pos = {
new_player_pos_x, new_player_pos_y, new_player_pos_x, new_player_pos_y,
player->position.tile_map_x, player->position.tile_map_y,
player->position.tile_x, player->position.tile_y player->position.tile_x, player->position.tile_y
}; };
player->position = recannonicalize_position( & world, new_pos ); player->position = recannonicalize_position( & world, new_pos );
} }
// player_tile_x
if ( player->jump_time > 0.f ) if ( player->jump_time > 0.f )
{ {
@ -898,12 +693,19 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
, 1.f, 0.24f, 0.24f ); , 1.f, 0.24f, 0.24f );
// Draw tilemap // Scrolling shitshow
for ( s32 row = 0; row < 9; ++ row ) #if 1
f32 center_x = 0.5f * scast(f32, back_buffer->width);
f32 center_y = 0.5f * scast(f32, back_buffer->height);
for ( s32 relative_row = -10; relative_row < +10; ++ relative_row )
{ {
for ( s32 col = 0; col < 16; ++ col ) for ( s32 relative_col = -20; relative_col < +20; ++ relative_col )
{ {
u32 tileID = tilemap_tile_value( current_tile_map, & world, col, row ); u32 col = player->position.tile_x + relative_col;
u32 row = player->position.tile_y + relative_row;
u32 tileID = piggy_get_tile_value( & world, col, row );
f32 color[3] = { 0.15f, 0.15f, 0.15f }; f32 color[3] = { 0.15f, 0.15f, 0.15f };
if ( tileID == 1 ) if ( tileID == 1 )
@ -920,17 +722,15 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
color[2] = 0.3f; color[2] = 0.3f;
} }
f32 min_x = world.tile_upper_left_x + scast(f32, col) * tile_size_in_pixels; f32 min_x = center_x + scast(f32, relative_col) * tile_size_in_pixels;
f32 min_y = world.tile_upper_left_y + scast(f32, row) * tile_size_in_pixels; f32 min_y = center_y - scast(f32, relative_row) * tile_size_in_pixels;
// f32 min_x = scast(f32, col) * tile_size_in_pixels; f32 max_x = min_x + tile_size_in_pixels;
// f32 min_y = scast(f32, row) * tile_size_in_pixels; f32 max_y = min_y - tile_size_in_pixels;
f32 max_x = min_x + tile_size_in_pixels;
f32 max_y = min_y + tile_size_in_pixels;
draw_rectangle( back_buffer draw_rectangle( back_buffer
, min_x, min_y , min_x, max_y
, max_x, max_y , max_x, min_y
, color[0], color[1], color[2] ); , color[0], color[1], color[2] );
} }
} }
@ -939,16 +739,88 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
f32 player_green = 0.7f; f32 player_green = 0.7f;
f32 player_blue = 0.3f; f32 player_blue = 0.3f;
f32 player_tile_x_offset = scast(f32, player->position.tile_x * world.tile_size_in_pixels); f32 player_tile_x_offset = center_x + scast(f32, player->position.tile_x) * world.tile_meters_to_pixels;
f32 player_tile_y_offset = scast(f32, player->position.tile_y * world.tile_size_in_pixels); f32 player_tile_y_offset = center_y - scast(f32, player->position.tile_y) * world.tile_meters_to_pixels;
f32 player_screen_pos_x = world.tile_upper_left_x + player_tile_x_offset + player->position.x * world.tile_meters_to_pixels; f32 player_screen_pos_x = center_x + player->position.x * world.tile_meters_to_pixels;
f32 player_screen_pos_y = world.tile_upper_left_y + player_tile_y_offset + player->position.y * world.tile_meters_to_pixels; f32 player_screen_pos_y = center_y - player->position.y * world.tile_meters_to_pixels;
// player_min_x = player_tile_x_offset - player_half_width * world;
draw_rectangle( back_buffer draw_rectangle( back_buffer
, player_screen_pos_x - player_half_width * world.tile_meters_to_pixels, player_screen_pos_y - player->height * world.tile_meters_to_pixels , player_screen_pos_x - player_half_width * world.tile_meters_to_pixels, player_screen_pos_y - player->height * world.tile_meters_to_pixels
, player_screen_pos_x + player_half_width * world.tile_meters_to_pixels, player_screen_pos_y , player_screen_pos_x + player_half_width * world.tile_meters_to_pixels, player_screen_pos_y
, player_red, player_green, player_blue ); , player_red, player_green, player_blue );
#endif
// Non-scrolling screen
#if 0
// Draw TileChunk
for ( u32 row = 0; row < 9; ++ row )
{
for ( u32 col = 0; col < 16; ++ col )
{
u32 tileID = piggy_get_tile_value( & world, col, row );
f32 color[3] = { 0.15f, 0.15f, 0.15f };
if ( tileID == 1 )
{
color[0] = 0.22f;
color[1] = 0.22f;
color[2] = 0.22f;
}
if ( row == player->position.tile_y && col == player->position.tile_x )
{
color[0] = 0.44f;
color[1] = 0.3f;
color[2] = 0.3f;
}
#if 0
// Was able to flip Y properly but not get the center alignemnt with the world.tile_lower_left that I wanted..
f32 min_x = world.tile_lower_left_x + scast(f32, col) * tile_size_in_pixels;
f32 min_y = scast(f32, row) * tile_size_in_pixels;
f32 max_x = min_x + tile_size_in_pixels;
f32 max_y = min_y + tile_size_in_pixels;
draw_rectangle( back_buffer
, min_x, min_y
, max_x, max_y
//, min_x, max_y
//, max_x, min_y
, color[0], color[1], color[2] );
#else
f32 min_x = world.tile_lower_left_x + scast(f32, col) * tile_size_in_pixels;
f32 min_y = world.tile_lower_left_y - scast(f32, row) * tile_size_in_pixels;
f32 max_x = min_x + tile_size_in_pixels;
f32 max_y = min_y - tile_size_in_pixels;
draw_rectangle( back_buffer
, min_x, max_y
, max_x, min_y
, color[0], color[1], color[2] );
#endif
}
}
// Player
f32 player_red = 0.7f;
f32 player_green = 0.7f;
f32 player_blue = 0.3f;
f32 player_tile_x_offset = scast(f32, player->position.tile_x * world.tile_size_in_pixels);
f32 player_tile_y_offset = -scast(f32, player->position.tile_y * world.tile_size_in_pixels);
f32 player_screen_pos_x = world.tile_lower_left_x + player_tile_x_offset + player->position.x * world.tile_meters_to_pixels;
f32 player_screen_pos_y = world.tile_lower_left_y + player_tile_y_offset - player->position.y * world.tile_meters_to_pixels;
draw_rectangle( back_buffer
, player_screen_pos_x - player_half_width * world.tile_meters_to_pixels, player_screen_pos_y - player->height * world.tile_meters_to_pixels
, player_screen_pos_x + player_half_width * world.tile_meters_to_pixels, player_screen_pos_y
, player_red, player_green, player_blue );
#endif
// Auto-Snapshot percent bar // Auto-Snapshot percent bar
if (1) if (1)

View File

@ -282,46 +282,59 @@ struct RecordedInput
}; };
#endif #endif
struct TileMap struct TileChunk
{ {
u32* tiles; u32* tiles;
}; };
struct World struct World
{ {
// TODO(Ed) : Remove
f32 tile_lower_left_x;
f32 tile_lower_left_y;
u32 chunk_shift;
u32 chunk_mask;
f32 tile_size_in_meters; f32 tile_size_in_meters;
s32 tile_size_in_pixels; s32 tile_size_in_pixels;
f32 tile_meters_to_pixels; f32 tile_meters_to_pixels;
f32 tile_upper_left_x; u32 chunk_dimension;
f32 tile_upper_left_y;
s32 num_tiles_x; // Number of tiles on the x-axis for a tilemap.
s32 num_tiles_y; // Number of tiles on the y-axis for a tilemap.
// TODO(Ed) : Beginner's sparseness // TODO(Ed) : Beginner's sparseness
s32 tilemaps_num_x; s32 tile_chunks_num_x;
s32 tilemaps_num_y; s32 tile_chunks_num_y;
TileMap* tile_maps; TileChunk* tile_chunks;
}; };
struct CanonPosition /*
This is a "backend" transient datatype for handling lookup of tile data from "chunks" of tiles.
*/
struct TileChunkPosition
{
u32 tile_chunk_x;
u32 tile_chunk_y;
// "Chunk-relative (x, y)
u32 tile_x;
u32 tile_y;
};
struct WorldPosition
{ {
// TODO(Ed) : Should this be from the center of the tile? // TODO(Ed) : Should this be from the center of the tile?
f32 x; f32 x;
f32 y; f32 y;
/* TODO(Ed) : // "World-relative (x, y), AKA: Absolute Position, etc
Take the tile map x & y and the tile x & y
where there is some low bits for the tile index
and the high bits are the tile "page".
*/
s32 tile_map_x;
s32 tile_map_y;
s32 tile_x; u32 tile_x;
s32 tile_y; u32 tile_y;
}; };
NS_ENGINE_END NS_ENGINE_END

View File

@ -0,0 +1,196 @@
/* TODO(Ed) : Do this properly later
I shoved this in here to depollute the mess Casey is making in engine.cpp.
I'll set this up more properly later when I see a good time to clean things up.
Doing my best to follow his advice to leave cleaning to when things are more "cemented".
*/
#if INTELLISENSE_DIRECTIVES
#include "engine.hpp"
#endif
NS_ENGINE_BEGIN
using SnapshotFn = void ( Memory* memory, platform::ModuleAPI* platform_api );
internal
void load_engine_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
{
platform_api->memory_copy( memory->persistent, memory->total_size()
, memory->snapshots[ memory->active_snapshot_slot ].memory );
}
internal
void load_game_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
{
s32 slot = memory->active_snapshot_slot;
EngineState* state = rcast( EngineState*, memory->persistent );
void* persistent_slot = memory->snapshots[ slot ].memory;
void* transient_slot = rcast( Byte*, memory->snapshots[ slot ].memory ) + state->game_memory.persistent_size;
platform_api->memory_copy( state->game_memory.persistent, state->game_memory.persistent_size, persistent_slot );
platform_api->memory_copy( state->game_memory.transient, state->game_memory.transient_size, transient_slot );
}
internal
void take_engine_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
{
platform_api->memory_copy( memory->snapshots[ memory->active_snapshot_slot ].memory, memory->total_size(), memory->persistent );
memory->snapshots[ memory->active_snapshot_slot ].age = platform_api->get_wall_clock();
}
internal
void take_game_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
{
s32 slot = memory->active_snapshot_slot;
EngineState* state = rcast( EngineState*, memory->persistent );
void* persistent_slot = memory->snapshots[ slot ].memory;
void* transient_slot = rcast( Byte*, memory->snapshots[ slot ].memory ) + state->game_memory.persistent_size;
platform_api->memory_copy( persistent_slot, state->game_memory.persistent_size, state->game_memory.persistent );
platform_api->memory_copy( transient_slot, state->game_memory.transient_size, state->game_memory.transient );
memory->snapshots[ memory->active_snapshot_slot ].age = platform_api->get_wall_clock();
}
internal
void begin_recording_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
Str file_name = str_ascii("test_input_");
StrPath file_path = {};
file_path.concat( platform_api->path_scratch, file_name );
snprintf( file_path.ptr, file_path.len, "%s%d.hm_replay", file_name.ptr, memory->active_snapshot_slot );
memory->active_input_replay_file.path = file_path;
platform_api->file_delete( memory->active_input_replay_file.path );
memory->replay_mode = ReplayMode_Record;
}
internal
void end_recording_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
memory->replay_mode = ReplayMode_Off;
platform_api->file_close( & memory->active_input_replay_file );
}
internal
void begin_playback_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
Str file_name = str_ascii("test_input_");
StrPath file_path = {};
file_path.concat( platform_api->path_scratch, file_name );
snprintf( file_path.ptr, file_path.len, "%s%d.hm_replay", file_name.ptr, memory->active_snapshot_slot );
// TODO(Ed - From Casey): Recording system still seems to take too long
// on record start - find out what Windows is doing and if
// we can speed up / defer some of that processing.
if ( platform_api->file_check_exists( file_path ) )
{
memory->active_input_replay_file.path = file_path;
memory->replay_mode = ReplayMode_Playback;
}
else
{
// TODO(Ed) : Logging
}
}
internal
void end_playback_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
memory->replay_mode = ReplayMode_Off;
platform_api->file_close( & memory->active_input_replay_file );
}
InputStateSnapshot input_state_snapshot( InputState* input )
{
InputStateSnapshot snapshot = {};
for ( s32 idx = 0; idx < array_count( snapshot.controllers ); ++ idx )
{
ControllerState* controller = & input->controllers[idx];
if ( controller == nullptr )
continue;
if ( controller->ds_pad )
snapshot.controllers[idx].ds_pad = *controller->ds_pad;
if ( controller->xpad )
snapshot.controllers[idx].xpad = *controller->xpad;
if ( controller->keyboard )
{
snapshot.controllers[idx].keyboard = *controller->keyboard;
}
if ( controller->mouse )
snapshot.controllers[idx].mouse = *controller->mouse;
}
return snapshot;
}
internal
void record_input( Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
InputStateSnapshot snapshot = input_state_snapshot( input );
if ( platform_api->file_write_stream( & memory->active_input_replay_file, sizeof(snapshot), &snapshot ) == 0 )
{
// TODO(Ed) : Logging
}
}
internal
void play_input( SnapshotFn* load_snapshot, Memory* memory, InputState* input, platform::ModuleAPI* platform_api )
{
InputStateSnapshot new_input;
if ( platform_api->file_read_stream( & memory->active_input_replay_file, sizeof(InputStateSnapshot), & new_input ) == 0 )
{
load_snapshot( memory, platform_api );
platform_api->file_rewind( & memory->active_input_replay_file );
return;
}
for ( s32 idx = 0; idx < array_count( new_input.controllers ); ++ idx )
{
ControllerState* controller = & input->controllers[idx];
if ( controller == nullptr )
continue;
if ( controller->ds_pad )
*controller->ds_pad = new_input.controllers[idx].ds_pad;
if ( controller->xpad )
*controller->xpad = new_input.controllers[idx].xpad;
if ( controller->keyboard )
{
*controller->keyboard = new_input.controllers[idx].keyboard;
}
if ( controller->mouse )
*controller->mouse = new_input.controllers[idx].mouse;
}
}
void process_loop_mode( SnapshotFn* take_snapshot, SnapshotFn* load_snapshot
, Memory* memory, EngineState* state, InputState* input, platform::ModuleAPI* platform_api )
{
if ( memory->replay_mode == ReplayMode_Off )
{
take_snapshot( memory, platform_api );
begin_recording_input( memory, input, platform_api );
}
else if ( memory->replay_mode == ReplayMode_Playback )
{
end_playback_input( memory, input, platform_api );
load_snapshot( memory, platform_api );
}
else if ( memory->replay_mode == ReplayMode_Record )
{
end_recording_input( memory, input, platform_api );
load_snapshot( memory, platform_api );
begin_playback_input( memory, input, platform_api );
}
}
NS_ENGINE_END

View File

@ -78,7 +78,7 @@ struct PlayerState
//f32 pos_x; //f32 pos_x;
//f32 pos_y; //f32 pos_y;
engine::CanonPosition position; engine::WorldPosition position;
b32 mid_jump; b32 mid_jump;
f32 jump_time; f32 jump_time;

View File

@ -6,22 +6,28 @@
// TODO(Ed) : Convert all of these to platform-efficient versions // TODO(Ed) : Convert all of these to platform-efficient versions
//inline
//s32 abs( s32 value )
//{
// return ;
//}
inline inline
s32 floor_f32_to_s32( f32 value ) s32 floor( f32 value )
{ {
s32 result = scast(s32, floorf( value )); s32 result = scast(s32, floorf( value ));
return result; return result;
} }
inline inline
s32 round_f32_to_s32( f32 value ) s32 round( f32 value )
{ {
s32 result = scast(s32, value + 0.5f); s32 result = scast(s32, value + 0.5f);
return result; return result;
} }
inline inline
s32 truncate_f32_to_s32( f32 value ) s32 truncate( f32 value )
{ {
s32 result = scast(s32, value); s32 result = scast(s32, value);
return result; return result;

View File

@ -157,11 +157,11 @@ function build-engine
$should_build = check-ModuleForChanges $path_engine $should_build = check-ModuleForChanges $path_engine
if ( $should_build -eq $false ) { if ( $should_build -eq $false ) {
write-host "No changes detected in engine module, skipping build" -ForegroundColor Yellow write-host "No changes detected in engine module, skipping build" -ForegroundColor Yellow
return return $true
} }
$path_pdb_lock = Join-Path $path_binaries 'handmade_engine.pdb.lock' $path_pdb_lock = Join-Path $path_binaries 'handmade_engine.pdb.lock'
New-Item $path_pdb_lock -ItemType File -Force New-Item $path_pdb_lock -ItemType File -Force > $null
# Delete old PDBs # Delete old PDBs
[Array]$pdb_files = Get-ChildItem -Path $path_binaries -Filter "handmade_engine_*.pdb" [Array]$pdb_files = Get-ChildItem -Path $path_binaries -Filter "handmade_engine_*.pdb"
@ -191,12 +191,17 @@ function build-engine
$unit = Join-Path $path_project 'handmade_engine.cpp' $unit = Join-Path $path_project 'handmade_engine.cpp'
$dynamic_library = Join-Path $path_binaries 'handmade_engine.dll' $dynamic_library = Join-Path $path_binaries 'handmade_engine.dll'
build-simple $path_build $includes $compiler_args $linker_args $unit $dynamic_library $build_result = build-simple $path_build $includes $compiler_args $linker_args $unit $dynamic_library
Remove-Item $path_pdb_lock -Force Remove-Item $path_pdb_lock -Force
if ( $build_result -eq $false ) {
return $false
}
#region CodeGen Post-Build #region CodeGen Post-Build
if ( -not $handmade_process_active ) { if ( -not $handmade_process_active -and $build_result )
{
$path_engine_symbols = Join-Path $path_build 'handmade_engine.symbols' $path_engine_symbols = Join-Path $path_build 'handmade_engine.symbols'
# Create the symbol table # Create the symbol table
@ -285,36 +290,45 @@ function build-engine
$unit = Join-Path $path_codegen 'engine_postbuild_gen.cpp' $unit = Join-Path $path_codegen 'engine_postbuild_gen.cpp'
$executable = Join-Path $path_build 'engine_postbuild_gen.exe' $executable = Join-Path $path_build 'engine_postbuild_gen.exe'
build-simple $path_build $local:includes $compiler_args $linker_args $unit $executable if ( build-simple $path_build $local:includes $compiler_args $linker_args $unit $executable )
{
Push-Location $path_build Push-Location $path_build
$time_taken = Measure-Command { $time_taken = Measure-Command {
& $executable 2>&1 | ForEach-Object { & $executable 2>&1 | ForEach-Object {
write-host `t $_ -ForegroundColor Green write-host `t $_ -ForegroundColor Green
}
} }
} Pop-Location
Pop-Location
$path_generated_file = Join-Path $path_build 'engine_symbol_table.hpp' $path_generated_file = Join-Path $path_build 'engine_symbol_table.hpp'
move-item $path_generated_file (join-path $path_gen (split-path $path_generated_file -leaf)) -Force move-item $path_generated_file (join-path $path_gen (split-path $path_generated_file -leaf)) -Force
$should_format_gen = $true $should_format_gen = $true
return $true
}
return $false
} }
} }
if ( $engine ) { if ( $engine ) {
build-engine $build_result = build-engine
if ( $build_result -eq $false ) {
$path_csv = Join-Path $path_build 'engine_module_hashes.csv'
Remove-Item $path_csv -Force
}
} }
function build-platform function build-platform
{ {
if ( $handmade_process_active ) { if ( $handmade_process_active ) {
write-host "Handmade process is active, skipping platform build" -ForegroundColor Yellow write-host "Handmade process is active, skipping platform build" -ForegroundColor Yellow
return return $true
} }
$should_build = check-ModuleForChanges $path_platform $should_build = check-ModuleForChanges $path_platform
if ( $should_build -eq $false ) { if ( $should_build -eq $false ) {
write-host "No changes detected in platform module, skipping build" -ForegroundColor Yellow write-host "No changes detected in platform module, skipping build" -ForegroundColor Yellow
return return $true
} }
# CodeGen Pre-Build # CodeGen Pre-Build
@ -330,7 +344,7 @@ function build-platform
$path_platform_gen = Join-Path $path_platform 'gen' $path_platform_gen = Join-Path $path_platform 'gen'
if ( -not (Test-Path $path_platform_gen) ) { if ( -not (Test-Path $path_platform_gen) ) {
New-Item $path_platform_gen -ItemType Directory New-Item $path_platform_gen -ItemType Directory > $null
} }
$local:includes = $script:includes $local:includes = $script:includes
@ -346,7 +360,10 @@ function build-platform
$unit = Join-Path $path_codegen 'platform_gen.cpp' $unit = Join-Path $path_codegen 'platform_gen.cpp'
$executable = Join-Path $path_build 'platform_gen.exe' $executable = Join-Path $path_build 'platform_gen.exe'
build-simple $path_build $includes $compiler_args $linker_args $unit $executable $path_build $build_result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable $path_build
if ( $build_result -eq $false ) {
return $false
}
Push-Location $path_platform Push-Location $path_platform
$time_taken = Measure-Command { $time_taken = Measure-Command {
@ -384,10 +401,15 @@ function build-platform
$unit = Join-Path $path_project 'handmade_win32.cpp' $unit = Join-Path $path_project 'handmade_win32.cpp'
$executable = Join-Path $path_binaries 'handmade_win32.exe' $executable = Join-Path $path_binaries 'handmade_win32.exe'
build-simple $path_build $includes $compiler_args $linker_args $unit $executable return build-simple $path_build $includes $compiler_args $linker_args $unit $executable
} }
if ( $platform ) { if ( $platform ) {
build-platform $build_result = build-platform
if ( -not $build_result ) {
$path_csv = Join-Path $path_build 'platform_module_hashes.csv'
Remove-Item $path_csv -Force
}
} }
$path_jsl_dll = Join-Path $path_binaries 'JoyShockLibrary.dll' $path_jsl_dll = Join-Path $path_binaries 'JoyShockLibrary.dll'

Binary file not shown.

View File

@ -46,11 +46,13 @@ function run-compiler
} }
} }
if ( Test-Path($unit) ) { if ( $LASTEXITCODE -eq 0 ) {
write-host "$unit compile finished in $($time_taken.TotalMilliseconds) ms`n" write-host "$unit compile finished in $($time_taken.TotalMilliseconds) ms`n"
return $true
} }
else { else {
write-host "Compile failed for $unit`n" -ForegroundColor Red write-host "Compile failed for $unit`n" -ForegroundColor Red
return $false
} }
} }
@ -77,11 +79,13 @@ function run-linker
} }
} }
if ( Test-Path($binary) ) { if ( $LASTEXITCODE -eq 0 ) {
write-host "$binary linking finished in $($time_taken.TotalMilliseconds) ms`n" write-host "$binary linking finished in $($time_taken.TotalMilliseconds) ms`n"
return $true
} }
else { else {
write-host "Linking failed for $binary`n" -ForegroundColor Red write-host "Linking failed for $binary`n" -ForegroundColor Red
return $false
} }
} }
@ -160,6 +164,7 @@ if ( $vendor -match "clang" )
function build-simple function build-simple
{ {
param( [string]$path_output, [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary ) param( [string]$path_output, [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary )
$result = $false
#Write-Host "build-simple: clang" #Write-Host "build-simple: clang"
$object = $unit -replace '\.cpp', '.obj' $object = $unit -replace '\.cpp', '.obj'
@ -205,7 +210,9 @@ if ( $vendor -match "clang" )
$compiler_args += $includes | ForEach-Object { $flag_include + $_ } $compiler_args += $includes | ForEach-Object { $flag_include + $_ }
$compiler_args += $flag_compile, $unit $compiler_args += $flag_compile, $unit
run-compiler $compiler $unit $compiler_args if ( (run-compiler $compiler $unit $compiler_args) -eq $false ) {
return $false
}
$linker_args += @( $linker_args += @(
$flag_link_win_machine_64, $flag_link_win_machine_64,
@ -222,7 +229,7 @@ if ( $vendor -match "clang" )
} }
$linker_args += $object $linker_args += $object
run-linker $linker $binary $linker_args return run-linker $linker $binary $linker_args
# $compiler_args += $unit # $compiler_args += $unit
# $linker_args | ForEach-Object { # $linker_args | ForEach-Object {
@ -285,6 +292,7 @@ if ( $vendor -match "msvc" )
function build-simple function build-simple
{ {
param( [string]$path_output, [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary ) param( [string]$path_output, [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary )
$result = $false
#Write-Host "build-simple: msvc" #Write-Host "build-simple: msvc"
$object = $unit -replace '\.(cpp)$', '.obj' $object = $unit -replace '\.(cpp)$', '.obj'
@ -336,7 +344,9 @@ if ( $vendor -match "msvc" )
$compiler_args += $includes | ForEach-Object { $flag_include + $_ } $compiler_args += $includes | ForEach-Object { $flag_include + $_ }
$compiler_args += $flag_compile, $unit $compiler_args += $flag_compile, $unit
run-compiler $compiler $unit $compiler_args if ( (run-compiler $compiler $unit $compiler_args) -eq $false ) {
return $false;
}
$linker_args += @( $linker_args += @(
$flag_nologo, $flag_nologo,
@ -353,7 +363,7 @@ if ( $vendor -match "msvc" )
} }
$linker_args += $object $linker_args += $object
run-linker $linker $binary $linker_args return run-linker $linker $binary $linker_args
# $compiler_args += $unit # $compiler_args += $unit
# $compiler_args += $flag_linker # $compiler_args += $flag_linker