Compare commits
No commits in common. "master" and "memory_block" have entirely different histories.
master
...
memory_blo
@ -121,7 +121,6 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\AssetManager.c" />
|
||||
<ClCompile Include="src\Asteroid.c" />
|
||||
<ClCompile Include="src\Audio.c" />
|
||||
<ClCompile Include="src\Common.c" />
|
||||
<ClCompile Include="src\Debug.c" />
|
||||
@ -130,7 +129,11 @@
|
||||
<ClCompile Include="src\MemoryArena.c" />
|
||||
<ClCompile Include="src\Platform.c" />
|
||||
<ClCompile Include="src\Renderer.c" />
|
||||
<ClCompile Include="src\Ui.c" />
|
||||
<ClCompile Include="src\Shader.c" />
|
||||
<ClCompile Include="src\String.c" />
|
||||
<ClCompile Include="src\UserInterface.c" />
|
||||
<ClCompile Include="src\WorldTraveller.c" />
|
||||
<ClCompile Include="src\Texture.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="data\shaders\default.frag.glsl" />
|
||||
@ -140,19 +143,21 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\include\Dengine\Assets.h" />
|
||||
<ClInclude Include="src\include\Dengine\Asteroid.h" />
|
||||
<ClInclude Include="src\include\Dengine\Audio.h" />
|
||||
<ClInclude Include="src\include\Dengine\Debug.h" />
|
||||
<ClInclude Include="src\include\Dengine\Entity.h" />
|
||||
<ClInclude Include="src\include\Dengine\MemoryArena.h" />
|
||||
<ClInclude Include="src\include\Dengine\Platform.h" />
|
||||
<ClInclude Include="src\include\Dengine\AssetManager.h" />
|
||||
<ClInclude Include="src\include\Dengine\Common.h" />
|
||||
<ClInclude Include="src\include\Breakout\Game.h" />
|
||||
<ClInclude Include="src\include\Dengine\Entity.h" />
|
||||
<ClInclude Include="src\include\Dengine\Math.h" />
|
||||
<ClInclude Include="src\include\Dengine\OpenGL.h" />
|
||||
<ClInclude Include="src\include\Dengine\Renderer.h" />
|
||||
<ClInclude Include="src\include\Dengine\Ui.h" />
|
||||
<ClInclude Include="src\include\Dengine\Shader.h" />
|
||||
<ClInclude Include="src\include\Dengine\String.h" />
|
||||
<ClInclude Include="src\include\Dengine\Texture.h" />
|
||||
<ClInclude Include="src\include\Dengine\UserInterface.h" />
|
||||
<ClInclude Include="src\include\Dengine\WorldTraveller.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
@ -15,6 +15,15 @@
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\WorldTraveller.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Texture.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Shader.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Renderer.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -33,19 +42,19 @@
|
||||
<ClCompile Include="src\Audio.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\dengine.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\MemoryArena.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Asteroid.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Entity.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Ui.c">
|
||||
<ClCompile Include="src\dengine.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\UserInterface.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\String.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\MemoryArena.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
@ -59,9 +68,15 @@
|
||||
<ClInclude Include="src\include\Dengine\Common.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\include\Dengine\Shader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\include\Dengine\OpenGL.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\include\Dengine\Texture.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\include\Dengine\AssetManager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -71,6 +86,9 @@
|
||||
<ClInclude Include="src\include\Dengine\Renderer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\include\Dengine\Entity.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\include\Dengine\Math.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -92,13 +110,10 @@
|
||||
<ClInclude Include="src\include\Dengine\WorldTraveller.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\include\Dengine\Asteroid.h">
|
||||
<ClInclude Include="src\include\Dengine\UserInterface.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\include\Dengine\Entity.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\include\Dengine\Ui.h">
|
||||
<ClInclude Include="src\include\Dengine\String.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
|
46
README.md
46
README.md
@ -1,31 +1,41 @@
|
||||
# Dengine
|
||||
Dengine is a 2d game engine developed with OpenGL and low-level libraries in C. The main goal of Dengine is to facilitate learning from many aspects of Computer Science. Alongside Dengine a game is being built which guides the development of features for the game. Dengine is inspired by Handmade Hero, with a focus on developing as many features with minimal libraries, such that it is not too time-consuming and counter-productive to do so.
|
||||
|
||||
![Asteroids Demo](extern/_assets/20161130_dengine_asteroids_low_q.gif)
|
||||
|
||||
## Milestones
|
||||
Features/research that has been implemented at one point in the making of the engine.
|
||||
Milestones/features/capabilities that have been added to the engine.
|
||||
|
||||
- Asset Management (load data into intermediate formats)
|
||||
- Audio API from OpenAL
|
||||
- Basic 2D Game Mathematics/Physics (Vector, Trig, Explicit Euler)
|
||||
- Collision Detection (Single Axis Theorem, Minkowski Sum/Diff, AABB)
|
||||
- Debug Services & Displays
|
||||
- Entity Component Systems
|
||||
- IMGUI Systems
|
||||
- Input Parsing
|
||||
- Push-Buffer Memory Systems
|
||||
- Small Size Array Optimisations
|
||||
- Standard Library Replacements (e.g. atoi, strlen, itoa, string)
|
||||
- Texture Atlas Support, Runtime Font Packing
|
||||
- OpenGL 2D Rendering Pipeline & Batching Render Calls
|
||||
### "Handmade" API
|
||||
- Custom Standard Library: (improved as is required) (e.g. strlen, atoi, itoa)
|
||||
- Custom Math Library: (improved as is required)
|
||||
- Custom String Library
|
||||
- Custom Memory Allocator: alloc memory from preallocated blocks
|
||||
- Custom XML Parser: for reading game data config
|
||||
|
||||
### Rendering
|
||||
- Animation playback: from spritesheets
|
||||
- Batched rendering: minimise draw calls grouped on texture & color modulation
|
||||
- Generate on runtime bitmap font sheet from font file
|
||||
- Rendering sprites: through GLEW and GLFW to OpenGL programmable pipeline
|
||||
- Rendering rects and text
|
||||
|
||||
### Other
|
||||
- Audio API: through OpenAL and STB Vorbis for audio functionality
|
||||
- Audio streaming
|
||||
- Asset loading system using hashtables
|
||||
- Collision using AABB
|
||||
- Debug services/display
|
||||
- Input parsing, delayed, raw read
|
||||
- IMGUI style GUI: Buttons, textfields, scroll bars
|
||||
- Moveable windows
|
||||
- Primitive entity system
|
||||
- Primitive battle system
|
||||
|
||||
## Building
|
||||
Dengine is currently being developed using Visual Studio 2015. You can build the project by executing the **build.bat** in the src folder, or simply opening the project in Visual Studio and compiling.
|
||||
Dengine is currently being developed using Visual Studio 2015. You can build the project by executing the (preferred) **build.bat** in the src folder, or simply opening the project in Visual Studio and compiling.
|
||||
|
||||
Additionally, an OpenAL-Soft DLL is required which can be downloaded from the Window pre-built binaries of [OpenAL here.](http://kcat.strangesoft.net/openal.html)
|
||||
|
||||
NOTE: Assets are not included in the repository. This only excludes audio as game assets are rendered with vertexes.
|
||||
NOTE: Assets are not included in the repository.
|
||||
|
||||
## Dependencies
|
||||
- [GLEW](http://glew.sourceforge.net/): Cross platform OpenGL wrapper.
|
||||
|
Binary file not shown.
@ -0,0 +1,12 @@
|
||||
#version 330 core
|
||||
in vec3 OurColor;
|
||||
in vec2 TexCoord;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D ourTexture1;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(ourTexture1, TexCoord);
|
||||
}
|
@ -1,9 +1,19 @@
|
||||
#version 330 core
|
||||
layout(location = 0) in vec4 data; // (vec2)pos, (vec2)texCoord
|
||||
|
||||
layout (location = 0) in vec3 position;
|
||||
layout (location = 1) in vec3 color;
|
||||
layout (location = 2) in vec2 texCoord;
|
||||
|
||||
out vec3 OurColor;
|
||||
out vec2 TexCoord;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * vec4(data.xy, 0.0f, 1.0f);
|
||||
gl_Position = projection * view * model * vec4(position, 1.0f);
|
||||
OurColor = color;
|
||||
TexCoord = texCoord;
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
uniform vec4 spriteColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = spriteColor;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#version 330 core
|
||||
layout(location = 0) in vec4 data; // (vec2)pos, (vec2)texCoord
|
||||
|
||||
uniform mat4 projection;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * vec4(data.xy, 0.0f, 1.0f);
|
||||
}
|
BIN
extern/_assets/20161130_dengine_asteroids_low_q.gif
vendored
BIN
extern/_assets/20161130_dengine_asteroids_low_q.gif
vendored
Binary file not shown.
Before Width: | Height: | Size: 1.8 MiB |
@ -22,111 +22,6 @@
|
||||
#include "Dengine/OpenGL.h"
|
||||
#include "Dengine/Platform.h"
|
||||
|
||||
enum BytesPerPixel
|
||||
{
|
||||
bytesPerPixel_Greyscale = 1,
|
||||
bytesPerPixel_GreyscaleAlpha = 2,
|
||||
bytesPerPixel_RGB = 3,
|
||||
bytesPerPixel_RGBA = 4,
|
||||
};
|
||||
|
||||
INTERNAL GLint getGLFormat(i32 bytesPerPixel, b32 srgb)
|
||||
{
|
||||
switch (bytesPerPixel)
|
||||
{
|
||||
case bytesPerPixel_Greyscale:
|
||||
return GL_LUMINANCE;
|
||||
case bytesPerPixel_GreyscaleAlpha:
|
||||
return GL_LUMINANCE_ALPHA;
|
||||
case bytesPerPixel_RGB:
|
||||
return (srgb ? GL_SRGB : GL_RGB);
|
||||
case bytesPerPixel_RGBA:
|
||||
return (srgb ? GL_SRGB_ALPHA : GL_RGBA);
|
||||
default:
|
||||
// TODO(doyle): Invalid
|
||||
// std::cout << "getGLFormat() invalid bytesPerPixel: "
|
||||
// << bytesPerPixel << std::endl;
|
||||
return GL_LUMINANCE;
|
||||
}
|
||||
}
|
||||
|
||||
Texture textureGen(const GLuint width, const GLuint height,
|
||||
const GLint bytesPerPixel, const u8 *const image)
|
||||
{
|
||||
// TODO(doyle): Let us set the parameters gl params as well
|
||||
GL_CHECK_ERROR();
|
||||
Texture tex = {0};
|
||||
tex.width = width;
|
||||
tex.height = height;
|
||||
tex.internalFormat = GL_RGBA;
|
||||
tex.wrapS = GL_REPEAT;
|
||||
tex.wrapT = GL_REPEAT;
|
||||
tex.filterMinification = GL_NEAREST;
|
||||
tex.filterMagnification = GL_NEAREST;
|
||||
|
||||
glGenTextures(1, &tex.id);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex.id);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
/* Load image into texture */
|
||||
// TODO(doyle) Figure out the gl format
|
||||
tex.imageFormat = getGLFormat(bytesPerPixel, FALSE);
|
||||
ASSERT(tex.imageFormat == GL_RGBA);
|
||||
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, tex.internalFormat, tex.width, tex.height, 0,
|
||||
tex.imageFormat, GL_UNSIGNED_BYTE, image);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
// TODO(doyle): Not needed for sprites? glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
/* Set parameter of currently bound texture */
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex.wrapS);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex.wrapT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
tex.filterMinification);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
|
||||
tex.filterMagnification);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
/* Unbind and clean up */
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
void asset_init(AssetManager *assetManager, MemoryArena_ *arena)
|
||||
{
|
||||
i32 texAtlasEntries = 8;
|
||||
assetManager->texAtlas.size = texAtlasEntries;
|
||||
assetManager->texAtlas.entries =
|
||||
memory_pushBytes(arena, texAtlasEntries * sizeof(HashTableEntry));
|
||||
|
||||
i32 animEntries = 1024;
|
||||
assetManager->anims.size = animEntries;
|
||||
assetManager->anims.entries =
|
||||
memory_pushBytes(arena, animEntries * sizeof(HashTableEntry));
|
||||
|
||||
i32 texEntries = 32;
|
||||
assetManager->textures.size = texEntries;
|
||||
assetManager->textures.entries =
|
||||
memory_pushBytes(arena, texEntries * sizeof(HashTableEntry));
|
||||
|
||||
/* Create empty 1x1 4bpp black texture */
|
||||
u32 bitmap = (0xFF << 24) | (0xFF << 16) | (0xFF << 8) | (0xFF << 0);
|
||||
Texture *tex = asset_texGetFreeSlot(assetManager, arena, "nullTex");
|
||||
*tex = textureGen(1, 1, 4, CAST(u8 *)(&bitmap));
|
||||
|
||||
i32 audioEntries = 32;
|
||||
assetManager->audio.size = audioEntries;
|
||||
assetManager->audio.entries =
|
||||
memory_pushBytes(arena, audioEntries * sizeof(HashTableEntry));
|
||||
}
|
||||
|
||||
/*
|
||||
*********************************
|
||||
* Hash Table Operations
|
||||
@ -205,7 +100,7 @@ INTERNAL SubTexture *getFreeAtlasSubTexSlot(TexAtlas *const atlas,
|
||||
}
|
||||
}
|
||||
|
||||
const SubTexture asset_atlasGetSubTex(TexAtlas *const atlas, const char *const key)
|
||||
const SubTexture asset_getAtlasSubTex(TexAtlas *const atlas, const char *const key)
|
||||
{
|
||||
|
||||
HashTableEntry *entry = getEntryFromHash(&atlas->subTex, key);
|
||||
@ -217,11 +112,11 @@ const SubTexture asset_atlasGetSubTex(TexAtlas *const atlas, const char *const k
|
||||
return result;
|
||||
}
|
||||
|
||||
DEBUG_LOG("asset_atlasGetSubTex() failed: Sub texture does not exist");
|
||||
DEBUG_LOG("asset_getAtlasSubTex() failed: Sub texture does not exist");
|
||||
return result;
|
||||
}
|
||||
|
||||
Texture *asset_texGet(AssetManager *const assetManager, const char *const key)
|
||||
Texture *asset_getTex(AssetManager *const assetManager, const char *const key)
|
||||
{
|
||||
HashTableEntry *entry = getEntryFromHash(&assetManager->textures, key);
|
||||
|
||||
@ -231,7 +126,7 @@ Texture *asset_texGet(AssetManager *const assetManager, const char *const key)
|
||||
return result;
|
||||
}
|
||||
|
||||
TexAtlas *asset_atlasGetFreeSlot(AssetManager *const assetManager,
|
||||
TexAtlas *asset_getFreeTexAtlasSlot(AssetManager *const assetManager,
|
||||
MemoryArena_ *arena, const char *const key,
|
||||
i32 numSubTex)
|
||||
{
|
||||
@ -268,7 +163,7 @@ TexAtlas *asset_atlasGetFreeSlot(AssetManager *const assetManager,
|
||||
}
|
||||
}
|
||||
|
||||
TexAtlas *asset_atlasGet(AssetManager *const assetManager,
|
||||
TexAtlas *asset_getTexAtlas(AssetManager *const assetManager,
|
||||
const char *const key)
|
||||
{
|
||||
|
||||
@ -281,7 +176,7 @@ TexAtlas *asset_atlasGet(AssetManager *const assetManager,
|
||||
}
|
||||
|
||||
|
||||
Texture *asset_texGetFreeSlot(AssetManager *const assetManager,
|
||||
Texture *asset_getFreeTexSlot(AssetManager *const assetManager,
|
||||
MemoryArena_ *const arena, const char *const key)
|
||||
{
|
||||
|
||||
@ -300,51 +195,39 @@ Texture *asset_texGetFreeSlot(AssetManager *const assetManager,
|
||||
}
|
||||
}
|
||||
|
||||
Texture *asset_texLoadImage(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
Texture *asset_loadTextureImage(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const char *const path, const char *const key)
|
||||
{
|
||||
/* Open the texture image */
|
||||
i32 imgWidth, imgHeight, bytesPerPixel;
|
||||
stbi_set_flip_vertically_on_load(TRUE);
|
||||
u8 *image =
|
||||
asset_imageLoad(&imgWidth, &imgHeight, &bytesPerPixel, path, TRUE);
|
||||
stbi_load(path, &imgWidth, &imgHeight, &bytesPerPixel, 0);
|
||||
|
||||
Texture *result = NULL;
|
||||
if (image)
|
||||
{
|
||||
result = asset_texGetFreeSlot(assetManager, arena, key);
|
||||
*result = textureGen(CAST(GLuint)(imgWidth), CAST(GLuint)(imgHeight),
|
||||
CAST(GLint)(bytesPerPixel), image);
|
||||
|
||||
GL_CHECK_ERROR();
|
||||
asset_imageFree(image);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u8 *asset_imageLoad(i32 *width, i32 *height, i32 *bpp, const char *const path,
|
||||
b32 flipVertically)
|
||||
{
|
||||
stbi_set_flip_vertically_on_load(flipVertically);
|
||||
u8 *image = stbi_load(path, width, height, bpp, 0);
|
||||
|
||||
if (*width != *height)
|
||||
#ifdef DENGINE_DEBUG
|
||||
if (imgWidth != imgHeight)
|
||||
{
|
||||
printf(
|
||||
"asset_texLoadImage() warning: Sprite sheet is not square: "
|
||||
"%dx%dpx\n",
|
||||
*width, *height);
|
||||
"asset_loadTextureImage() warning: Sprite sheet is not square: "
|
||||
"%dx%dpx\n", imgWidth, imgHeight);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!image)
|
||||
{
|
||||
printf("stdbi_load() failed: %s\n", stbi_failure_reason());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
Texture *result = asset_getFreeTexSlot(assetManager, arena, key);
|
||||
*result = texture_gen(CAST(GLuint)(imgWidth), CAST(GLuint)(imgHeight),
|
||||
CAST(GLint)(bytesPerPixel), image);
|
||||
|
||||
void asset_imageFree(u8 *image) { stbi_image_free(image); }
|
||||
GL_CHECK_ERROR();
|
||||
stbi_image_free(image);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
*********************************
|
||||
@ -369,7 +252,7 @@ INTERNAL Animation *getFreeAnimationSlot(AssetManager *const assetManager,
|
||||
}
|
||||
}
|
||||
|
||||
void asset_animAdd(AssetManager *const assetManager,
|
||||
void asset_addAnimation(AssetManager *const assetManager,
|
||||
MemoryArena_ *const arena, const char *const animName,
|
||||
TexAtlas *const atlas,
|
||||
char **const subTextureNames,
|
||||
@ -393,7 +276,7 @@ void asset_animAdd(AssetManager *const assetManager,
|
||||
|
||||
}
|
||||
|
||||
Animation *asset_animGet(AssetManager *const assetManager,
|
||||
Animation *asset_getAnim(AssetManager *const assetManager,
|
||||
const char *const key)
|
||||
{
|
||||
HashTableEntry *entry = getEntryFromHash(&assetManager->anims, key);
|
||||
@ -706,7 +589,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena_ *arena
|
||||
*/
|
||||
char *imageName = atlasXmlNode->attribute.value;
|
||||
i32 numSubTex = 1024;
|
||||
atlas = asset_atlasGetFreeSlot(assetManager, arena,
|
||||
atlas = asset_getFreeTexAtlasSlot(assetManager, arena,
|
||||
imageName, numSubTex);
|
||||
|
||||
if (!atlas)
|
||||
@ -732,7 +615,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena_ *arena
|
||||
common_strncat(imagePath, dataDir, dataDirLen);
|
||||
common_strncat(imagePath, imageName, imageNameLen);
|
||||
|
||||
Texture *tex = asset_texLoadImage(assetManager, arena,
|
||||
Texture *tex = asset_loadTextureImage(assetManager, arena,
|
||||
imagePath, imageName);
|
||||
|
||||
if (!tex)
|
||||
@ -783,7 +666,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena_ *arena
|
||||
i32 valueLen = common_strlen(value);
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
|
||||
subTex.rect.min.x = CAST(f32) intValue;
|
||||
subTex.rect.pos.x = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTexAttrib->name, "y") ==
|
||||
0)
|
||||
@ -792,7 +675,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena_ *arena
|
||||
i32 valueLen = common_strlen(value);
|
||||
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
subTex.rect.min.y = CAST(f32) intValue;
|
||||
subTex.rect.pos.y = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTexAttrib->name,
|
||||
"width") == 0)
|
||||
@ -801,7 +684,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena_ *arena
|
||||
i32 valueLen = common_strlen(value);
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
|
||||
subTex.rect.max.w = CAST(f32) intValue;
|
||||
subTex.rect.size.w = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTexAttrib->name,
|
||||
"height") == 0)
|
||||
@ -810,7 +693,7 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena_ *arena
|
||||
i32 valueLen = common_strlen(value);
|
||||
i32 intValue = common_atoi(value, valueLen);
|
||||
|
||||
subTex.rect.max.h = CAST(f32) intValue;
|
||||
subTex.rect.size.h = CAST(f32) intValue;
|
||||
}
|
||||
else if (common_strcmp(subTexAttrib->name,
|
||||
"hand_offset_x") == 0)
|
||||
@ -843,9 +726,9 @@ INTERNAL void parseXmlTreeToGame(AssetManager *assetManager, MemoryArena_ *arena
|
||||
// TODO(doyle): XML specifies 0,0 top left, we
|
||||
// prefer 0,0 bottom right, so offset by size since 0,0
|
||||
// is top left and size creates a bounding box below it
|
||||
subTex.rect.min.y = 1024 - subTex.rect.min.y;
|
||||
subTex.rect.min.y -= subTex.rect.max.h;
|
||||
subTex.offset.y = subTex.rect.max.h - subTex.offset.y;
|
||||
subTex.rect.pos.y = 1024 - subTex.rect.pos.y;
|
||||
subTex.rect.pos.y -= subTex.rect.size.h;
|
||||
subTex.offset.y = subTex.rect.size.h - subTex.offset.y;
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(key);
|
||||
@ -928,7 +811,7 @@ INTERNAL void freeXmlData(MemoryArena_ *const arena, XmlToken *tokens,
|
||||
* Everything else
|
||||
*********************************
|
||||
*/
|
||||
const i32 asset_xmlLoad(AssetManager *const assetManager,
|
||||
const i32 asset_loadXmlFile(AssetManager *const assetManager,
|
||||
MemoryArena_ *const arena,
|
||||
const PlatformFileRead *const fileRead)
|
||||
{
|
||||
@ -957,7 +840,7 @@ const i32 asset_xmlLoad(AssetManager *const assetManager,
|
||||
return result;
|
||||
}
|
||||
|
||||
AudioVorbis *const asset_vorbisGet(AssetManager *const assetManager,
|
||||
AudioVorbis *const asset_getVorbis(AssetManager *const assetManager,
|
||||
const char *const key)
|
||||
{
|
||||
|
||||
@ -969,7 +852,7 @@ AudioVorbis *const asset_vorbisGet(AssetManager *const assetManager,
|
||||
return result;
|
||||
}
|
||||
|
||||
const i32 asset_vorbisLoad(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const char *const path, const char *const key)
|
||||
{
|
||||
HashTableEntry *entry = getFreeHashSlot(&assetManager->audio, arena, key);
|
||||
@ -1035,45 +918,43 @@ INTERNAL GLuint createShaderFromPath(MemoryArena_ *arena, const char *const path
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL u32 shaderLoadProgram(const GLuint vertexShader,
|
||||
INTERNAL i32 shaderLoadProgram(Shader *const shader, const GLuint vertexShader,
|
||||
const GLuint fragmentShader)
|
||||
{
|
||||
u32 result = glCreateProgram();
|
||||
glAttachShader(result, vertexShader);
|
||||
glAttachShader(result, fragmentShader);
|
||||
glLinkProgram(result);
|
||||
GL_CHECK_ERROR();
|
||||
shader->id = glCreateProgram();
|
||||
glAttachShader(shader->id, vertexShader);
|
||||
glAttachShader(shader->id, fragmentShader);
|
||||
glLinkProgram(shader->id);
|
||||
|
||||
glDeleteShader(fragmentShader);
|
||||
glDeleteShader(vertexShader);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
GLint success;
|
||||
GLchar infoLog[512];
|
||||
glGetProgramiv(result, GL_LINK_STATUS, &success);
|
||||
GL_CHECK_ERROR();
|
||||
if (result == 0)
|
||||
glGetProgramiv(shader->id, GL_LINK_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
glGetProgramInfoLog(result, 512, NULL, infoLog);
|
||||
glGetProgramInfoLog(shader->id, 512, NULL, infoLog);
|
||||
printf("glLinkProgram failed: %s\n", infoLog);
|
||||
ASSERT(TRUE);
|
||||
return -1;
|
||||
}
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 asset_shaderGet(AssetManager *assetManager, const enum ShaderList type)
|
||||
Shader *const asset_getShader(AssetManager *assetManager,
|
||||
const enum ShaderList type)
|
||||
{
|
||||
if (type < shaderlist_count) return assetManager->shaders[type];
|
||||
if (type < shaderlist_count)
|
||||
return &assetManager->shaders[type];
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
#endif
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const i32 asset_shaderLoad(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const i32 asset_loadShaderFiles(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const char *const vertexPath,
|
||||
const char *const fragmentPath,
|
||||
const enum ShaderList type)
|
||||
@ -1082,89 +963,15 @@ const i32 asset_shaderLoad(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
GLuint fragmentShader =
|
||||
createShaderFromPath(arena, fragmentPath, GL_FRAGMENT_SHADER);
|
||||
|
||||
u32 shaderId = shaderLoadProgram(vertexShader, fragmentShader);
|
||||
if (shaderId == 0) return -1;
|
||||
Shader shader;
|
||||
i32 result = shaderLoadProgram(&shader, vertexShader, fragmentShader);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
assetManager->shaders[type] = shaderId;
|
||||
assetManager->shaders[type] = shader;
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERNAL FontPack *getMatchingFontPack(AssetManager *assetManager,
|
||||
const char *const name)
|
||||
{
|
||||
FontPack *result = NULL;
|
||||
for (i32 i = 0; i < ARRAY_COUNT(assetManager->fontPack); i++)
|
||||
{
|
||||
FontPack *checkFontPack = &assetManager->fontPack[i];
|
||||
if (common_strcmp(checkFontPack->name, name) == 0)
|
||||
{
|
||||
result = checkFontPack;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Font *asset_fontGetOrCreateOnDemand(AssetManager *assetManager,
|
||||
MemoryArena_ *persistentArena,
|
||||
MemoryArena_ *transientArena, char *name,
|
||||
i32 size)
|
||||
{
|
||||
|
||||
Font *result = asset_fontGet(assetManager, name, size);
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
FontPack *pack = getMatchingFontPack(assetManager, name);
|
||||
if (pack != NULL)
|
||||
{
|
||||
for(i32 i = 0; i < pack->fontIndex; i++)
|
||||
{
|
||||
Font *checkFont = &pack->font[i];
|
||||
if (checkFont->size == size)
|
||||
{
|
||||
result = checkFont;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
asset_fontLoadTTF(assetManager, persistentArena, transientArena,
|
||||
pack->filePath, name, size);
|
||||
|
||||
result = asset_fontGet(assetManager, name, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(doyle): Logging
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Font *asset_fontGet(AssetManager *assetManager, char *name, i32 size)
|
||||
{
|
||||
Font *result = NULL;
|
||||
FontPack *pack = getMatchingFontPack(assetManager, name);
|
||||
|
||||
if (pack != NULL) {
|
||||
for (i32 j = 0; j < ARRAY_COUNT(pack->font); j++)
|
||||
{
|
||||
if (pack->font[j].fontHeight == size)
|
||||
{
|
||||
result = &pack->font[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Individual glyph bitmap generated from STB used for creating a font sheet */
|
||||
typedef struct GlyphBitmap
|
||||
{
|
||||
@ -1173,77 +980,33 @@ typedef struct GlyphBitmap
|
||||
i32 codepoint;
|
||||
} GlyphBitmap;
|
||||
|
||||
const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
MemoryArena_ *persistentArena,
|
||||
MemoryArena_ *transientArena, char *filePath,
|
||||
char *name, i32 targetFontHeight)
|
||||
const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const char *filePath)
|
||||
{
|
||||
/*
|
||||
****************************************
|
||||
* Initialise assetmanager font reference
|
||||
****************************************
|
||||
*/
|
||||
FontPack *fontPack = NULL;
|
||||
for (i32 i = 0; i < assetManager->fontPackIndex; i++)
|
||||
{
|
||||
FontPack *checkFontPack = &assetManager->fontPack[i];
|
||||
if (common_strcmp(checkFontPack->name, name) == 0)
|
||||
{
|
||||
fontPack = checkFontPack;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fontPack == NULL) {
|
||||
fontPack = &assetManager->fontPack[assetManager->fontPackIndex++];
|
||||
fontPack->name = name;
|
||||
fontPack->filePath = filePath;
|
||||
ASSERT(assetManager->fontPackIndex <
|
||||
ARRAY_COUNT(assetManager->fontPack));
|
||||
}
|
||||
|
||||
Font *font = NULL;
|
||||
for (i32 j = 0; j < fontPack->fontIndex; j++)
|
||||
{
|
||||
Font *checkFont = &fontPack->font[j];
|
||||
if (checkFont->fontHeight == targetFontHeight)
|
||||
{
|
||||
font = &fontPack->font[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (font == NULL)
|
||||
{
|
||||
font = &fontPack->font[fontPack->fontIndex++];
|
||||
ASSERT(fontPack->fontIndex < ARRAY_COUNT(fontPack->font));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
TempMemory tempRegion = memory_beginTempRegion(transientArena);
|
||||
|
||||
PlatformFileRead fontFileRead = {0};
|
||||
i32 result =
|
||||
platform_readFileToBuffer(transientArena, filePath, &fontFileRead);
|
||||
i32 result = platform_readFileToBuffer(arena, filePath, &fontFileRead);
|
||||
if (result) return result;
|
||||
|
||||
stbtt_fontinfo fontInfo = {0};
|
||||
stbtt_InitFont(&fontInfo, fontFileRead.buffer,
|
||||
stbtt_GetFontOffsetForIndex(fontFileRead.buffer, 0));
|
||||
|
||||
/*
|
||||
****************************************
|
||||
* Initialise assetmanager font reference
|
||||
****************************************
|
||||
*/
|
||||
Font *font = &assetManager->font;
|
||||
font->codepointRange = V2i(32, 127);
|
||||
v2 codepointRange = font->codepointRange;
|
||||
const i32 numGlyphs = CAST(i32)(codepointRange.y - codepointRange.x);
|
||||
|
||||
GlyphBitmap *glyphBitmaps =
|
||||
memory_pushBytes(transientArena, numGlyphs * sizeof(GlyphBitmap));
|
||||
memory_pushBytes(arena, numGlyphs * sizeof(GlyphBitmap));
|
||||
v2 largestGlyphDimension = V2(0, 0);
|
||||
|
||||
font->fontHeight = targetFontHeight;
|
||||
f32 scaleY = stbtt_ScaleForPixelHeight(&fontInfo, (f32)targetFontHeight);
|
||||
const f32 targetFontHeight = 15.0f;
|
||||
f32 scaleY = stbtt_ScaleForPixelHeight(&fontInfo, targetFontHeight);
|
||||
|
||||
i32 ascent, descent, lineGap;
|
||||
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
|
||||
@ -1255,7 +1018,7 @@ const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
font->metrics = CAST(FontMetrics){ascent, descent, lineGap};
|
||||
|
||||
font->charMetrics =
|
||||
memory_pushBytes(persistentArena, numGlyphs * sizeof(CharMetrics));
|
||||
memory_pushBytes(arena, numGlyphs * sizeof(CharMetrics));
|
||||
|
||||
/*
|
||||
************************************************************
|
||||
@ -1275,7 +1038,7 @@ const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
|
||||
u8 *source = monoBitmap;
|
||||
u32 *colorBitmap =
|
||||
memory_pushBytes(transientArena, width * height * sizeof(u32));
|
||||
memory_pushBytes(arena, width * height * sizeof(u32));
|
||||
u32 *dest = colorBitmap;
|
||||
|
||||
// NOTE(doyle): STB generates 1 byte per pixel bitmaps, we use 4bpp, so
|
||||
@ -1319,7 +1082,7 @@ const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
if ((largestGlyphDimension.h - CAST(i32)targetFontHeight) >= 50)
|
||||
{
|
||||
printf(
|
||||
"asset_fontLoadTTF() warning: The loaded font file has a glyph "
|
||||
"asset_loadTTFont() warning: The loaded font file has a glyph "
|
||||
"considerably larger than our target .. font packing is "
|
||||
"unoptimal\n");
|
||||
}
|
||||
@ -1343,7 +1106,7 @@ const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
if ((glyphsPerRow * glyphsPerCol) <= numGlyphs)
|
||||
{
|
||||
printf(
|
||||
"asset_fontLoadTTF() warning: The target font height creates a "
|
||||
"asset_loadTTFont() warning: The target font height creates a "
|
||||
"glyph sheet that exceeds the available space!");
|
||||
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
@ -1351,8 +1114,7 @@ const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
#endif
|
||||
|
||||
i32 bitmapSize = SQUARED(TARGET_TEXTURE_SIZE) * TARGET_BYTES_PER_PIXEL;
|
||||
u32 *fontBitmap =
|
||||
memory_pushBytes(transientArena, bitmapSize * sizeof(u32));
|
||||
u32 *fontBitmap = memory_pushBytes(arena, bitmapSize * sizeof(u32));
|
||||
const i32 pitch = MAX_TEXTURE_SIZE * TARGET_BYTES_PER_PIXEL;
|
||||
|
||||
// Check value to determine when a row of glyphs is completely printed
|
||||
@ -1369,8 +1131,8 @@ const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
char charToEncode = CAST(char)codepointRange.x;
|
||||
|
||||
i32 numSubTex = numGlyphs;
|
||||
TexAtlas *fontAtlas = asset_atlasGetFreeSlot(
|
||||
assetManager, persistentArena, "font", numSubTex);
|
||||
TexAtlas *fontAtlas =
|
||||
asset_getFreeTexAtlasSlot(assetManager, arena, "font", numSubTex);
|
||||
|
||||
/*
|
||||
*********************************************************
|
||||
@ -1397,8 +1159,7 @@ const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
// all ascii characters, charToEncode represents the character
|
||||
// 1:1
|
||||
const char key[2] = {charToEncode, 0};
|
||||
SubTexture *subTex =
|
||||
getFreeAtlasSubTexSlot(fontAtlas, persistentArena, key);
|
||||
SubTexture *subTex = getFreeAtlasSubTexSlot(fontAtlas, arena, key);
|
||||
subTex->rect = CAST(Rect){origin, font->maxSize};
|
||||
charToEncode++;
|
||||
}
|
||||
@ -1452,8 +1213,8 @@ const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
* Generate and store font bitmap to assets
|
||||
*******************************************
|
||||
*/
|
||||
Texture *tex = asset_texGetFreeSlot(assetManager, persistentArena, "font");
|
||||
*tex = textureGen(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4,
|
||||
Texture *tex = asset_getFreeTexSlot(assetManager, arena, "font");
|
||||
*tex = texture_gen(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4,
|
||||
CAST(u8 *) fontBitmap);
|
||||
|
||||
#ifdef WT_RENDER_FONT_FILE
|
||||
@ -1477,13 +1238,18 @@ const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
i32 glyphBitmapSizeInBytes = CAST(i32) glyphBitmaps[i].dimensions.w *
|
||||
CAST(i32) glyphBitmaps[i].dimensions.h *
|
||||
sizeof(u32);
|
||||
// TODO(doyle): Mem free
|
||||
// PLATFORM_MEM_FREE(arena, glyphBitmaps[i].pixels, glyphBitmapSizeInBytes);
|
||||
}
|
||||
|
||||
memory_endTempRegion(tempRegion);
|
||||
// TODO(doyle): Mem free
|
||||
// PLATFORM_MEM_FREE(arena, glyphBitmaps, numGlyphs * sizeof(GlyphBitmap));
|
||||
platform_closeFileRead(arena, &fontFileRead);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const v2 asset_fontStringDimInPixels(const Font *const font,
|
||||
const v2 asset_stringDimInPixels(const Font *const font,
|
||||
const char *const string)
|
||||
{
|
||||
v2 stringDim = V2(0, 0);
|
||||
|
1497
src/Asteroid.c
1497
src/Asteroid.c
File diff suppressed because it is too large
Load Diff
27
src/Audio.c
27
src/Audio.c
@ -129,7 +129,7 @@ INTERNAL i32 rendererAcquire(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
"rendererAcquire(): Renderer has not been released before "
|
||||
"acquiring, force release by stopping stream");
|
||||
#endif
|
||||
audio_vorbisStop(arena, audioManager, audioRenderer);
|
||||
audio_stopVorbis(arena, audioManager, audioRenderer);
|
||||
}
|
||||
|
||||
// TODO(doyle): Super bad linear O(n) search for every audio-enabled entity
|
||||
@ -239,14 +239,14 @@ INTERNAL i32 initRendererForPlayback(MemoryArena_ *arena,
|
||||
ASSERT(audioManager && audioRenderer && vorbis);
|
||||
if (numPlays != AUDIO_REPEAT_INFINITE && numPlays <= 0)
|
||||
{
|
||||
DEBUG_LOG("audio_vorbisStream() warning: Number of plays is less than 0");
|
||||
DEBUG_LOG("audio_streamPlayVorbis() warning: Number of plays is less than 0");
|
||||
}
|
||||
#endif
|
||||
|
||||
i32 result = rendererAcquire(arena, audioManager, audioRenderer);
|
||||
if (result)
|
||||
{
|
||||
DEBUG_LOG("audio_vorbisStream() failed: Could not acquire renderer");
|
||||
DEBUG_LOG("audio_streamPlayVorbis() failed: Could not acquire renderer");
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -258,7 +258,7 @@ INTERNAL i32 initRendererForPlayback(MemoryArena_ *arena,
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG(
|
||||
"audio_vorbisStream() warning: Unexpected channel format");
|
||||
"audio_streamPlayVorbis() warning: Unexpected channel format");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -268,12 +268,10 @@ INTERNAL i32 initRendererForPlayback(MemoryArena_ *arena,
|
||||
}
|
||||
|
||||
#include <stdlib.h>
|
||||
const i32 audio_vorbisPlay(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
const i32 audio_playVorbis(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer, AudioVorbis *vorbis,
|
||||
i32 numPlays)
|
||||
{
|
||||
if (vorbis == NULL) return -1;
|
||||
|
||||
i32 result = initRendererForPlayback(arena, audioManager, audioRenderer,
|
||||
vorbis, numPlays);
|
||||
|
||||
@ -296,16 +294,11 @@ const i32 audio_vorbisPlay(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
return result;
|
||||
}
|
||||
|
||||
const i32 audio_vorbisStream(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
const i32 audio_streamPlayVorbis(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer,
|
||||
AudioVorbis *vorbis, i32 numPlays)
|
||||
{
|
||||
|
||||
// TODO(doyle): Streaming leaks memory, we don't free the "copy audio"
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
|
||||
if (vorbis == NULL) return -1;
|
||||
|
||||
i32 result = initRendererForPlayback(arena, audioManager, audioRenderer,
|
||||
vorbis, numPlays);
|
||||
// NOTE(doyle): We make a copy of the audio vorbis file using all the same
|
||||
@ -326,7 +319,7 @@ const i32 audio_vorbisStream(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
return result;
|
||||
}
|
||||
|
||||
const i32 audio_vorbisStop(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
const i32 audio_stopVorbis(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer)
|
||||
{
|
||||
i32 result = 0;
|
||||
@ -354,7 +347,7 @@ const i32 audio_vorbisStop(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
return result;
|
||||
}
|
||||
|
||||
const i32 audio_vorbisPause(AudioManager *audioManager,
|
||||
const i32 audio_pauseVorbis(AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer)
|
||||
{
|
||||
i32 result = 0;
|
||||
@ -373,7 +366,7 @@ const i32 audio_vorbisPause(AudioManager *audioManager,
|
||||
return result;
|
||||
}
|
||||
|
||||
const i32 audio_vorbisResume(AudioManager *audioManager,
|
||||
const i32 audio_resumeVorbis(AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer)
|
||||
{
|
||||
i32 result = 0;
|
||||
@ -387,7 +380,7 @@ const i32 audio_vorbisResume(AudioManager *audioManager,
|
||||
else
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("audio_vorbisResume(): Tried to resume invalid source")
|
||||
DEBUG_LOG("audio_resumeVorbis(): Tried to resume invalid source")
|
||||
#endif
|
||||
result = -1;
|
||||
}
|
||||
|
208
src/Common.c
208
src/Common.c
@ -1,173 +1,10 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Dengine/Common.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
|
||||
void common_optimalArrayV2Create(OptimalArrayV2 *array)
|
||||
{
|
||||
array->ptr = array->fastStorage;
|
||||
array->size = ARRAY_COUNT(array->fastStorage);
|
||||
}
|
||||
|
||||
i32 common_optimalArrayV2Push(OptimalArrayV2 *array, v2 data)
|
||||
{
|
||||
if (array->index + 1 > array->size)
|
||||
{
|
||||
array->size += ARRAY_COUNT(array->fastStorage);
|
||||
i32 newSizeInBytes = array->size * sizeof(v2);
|
||||
|
||||
/* If first time expanding, we need to manually malloc and copy */
|
||||
if (array->ptr == array->fastStorage)
|
||||
{
|
||||
array->ptr = malloc(newSizeInBytes);
|
||||
for (i32 i = 0; i < ARRAY_COUNT(array->fastStorage); i++)
|
||||
{
|
||||
array->ptr[i] = array->fastStorage[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
array->ptr = realloc(array->ptr, newSizeInBytes);
|
||||
}
|
||||
|
||||
if (!array->ptr) return optimalarrayerror_out_of_memory;
|
||||
}
|
||||
|
||||
array->ptr[array->index++] = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void common_optimalArrayV2Destroy(OptimalArrayV2 *array)
|
||||
{
|
||||
if (array->ptr != array->fastStorage)
|
||||
{
|
||||
free(array->ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* +-------------------------------------+
|
||||
* | Header | C-String | Null Terminator |
|
||||
* +-------------------------------------+
|
||||
* |
|
||||
* +--> Functions return the c-string for compatibility with other
|
||||
* string libraries
|
||||
*
|
||||
* Headers are retrieved using pointer arithmetric from the C string. These
|
||||
* strings are typechecked by their own typedef char String.
|
||||
*/
|
||||
|
||||
typedef struct StringHeader
|
||||
{
|
||||
i32 len;
|
||||
|
||||
// NOTE(doyle): A string is stored as one contiguous chunk of memory. We
|
||||
// don't use a pointer for storing the string as this'd require an extra
|
||||
// 4 bytes to store the pointer, which we don't need if everything is
|
||||
// contiguous. The string follows on from the len, and we return the address
|
||||
// of the string to simulate a pointer.
|
||||
String string;
|
||||
} StringHeader;
|
||||
|
||||
// TODO(doyle): string capacity- append if already enough space
|
||||
INTERNAL StringHeader *stringGetHeader(String *const string)
|
||||
{
|
||||
StringHeader *result = NULL;
|
||||
|
||||
// NOTE(doyle): C-String must be located at end of struct type for offset to
|
||||
// be correct! We cannot just subtract the string-header since we start at
|
||||
// the string ptr position
|
||||
if (string)
|
||||
{
|
||||
i32 byteOffsetToHeader = sizeof(StringHeader) - sizeof(String *);
|
||||
result = CAST(StringHeader *)((CAST(u8 *) string) - byteOffsetToHeader);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
i32 common_stringLen(String *const string)
|
||||
{
|
||||
if (!string) return -1;
|
||||
|
||||
StringHeader *header = stringGetHeader(string);
|
||||
i32 result = header->len;
|
||||
return result;
|
||||
}
|
||||
|
||||
String *const common_stringAppend(MemoryArena_ *const arena, String *oldString,
|
||||
char *appendString, i32 appendLen)
|
||||
|
||||
{
|
||||
if (!oldString || !appendString || !arena) return oldString;
|
||||
|
||||
/* Calculate size of new string */
|
||||
StringHeader *oldHeader = stringGetHeader(oldString);
|
||||
i32 newLen = oldHeader->len + appendLen;
|
||||
String *newString = common_stringMakeLen(arena, newLen);
|
||||
|
||||
/* Append strings together */
|
||||
String *insertPtr = newString;
|
||||
common_strncpy(insertPtr, oldString, oldHeader->len);
|
||||
insertPtr += oldHeader->len;
|
||||
common_strncpy(insertPtr, appendString, appendLen);
|
||||
|
||||
/* Free old string */
|
||||
common_stringFree(arena, oldString);
|
||||
|
||||
return newString;
|
||||
}
|
||||
|
||||
void common_stringFree(MemoryArena_ *arena, String *string)
|
||||
{
|
||||
if (!string || !arena) return;
|
||||
|
||||
StringHeader *header = stringGetHeader(string);
|
||||
i32 bytesToFree = sizeof(StringHeader) + header->len;
|
||||
|
||||
common_memset((u8 *)header, 0, bytesToFree);
|
||||
|
||||
// TODO(doyle): Mem free
|
||||
// PLATFORM_MEM_FREE(arena, header, bytesToFree);
|
||||
|
||||
string = NULL;
|
||||
}
|
||||
|
||||
String *const common_stringMake(MemoryArena_ *const arena, char *string)
|
||||
{
|
||||
if (!arena) return NULL;
|
||||
|
||||
i32 len = common_strlen(string);
|
||||
String *result = common_stringMakeLen(arena, len);
|
||||
common_strncpy(result, string, len);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String *const common_stringMakeLen(MemoryArena_ *const arena, i32 len)
|
||||
{
|
||||
if (!arena) return NULL;
|
||||
|
||||
// NOTE(doyle): Allocate the string header size plus the len. But _note_
|
||||
// that StringHeader contains a single String character. This has
|
||||
// a side-effect of already preallocating a byte for the null-terminating
|
||||
// character. Whilst the len of a string counts up to the last character
|
||||
// _not_ including null-terminator.
|
||||
i32 bytesToAllocate = sizeof(StringHeader) + len;
|
||||
void *chunk = memory_pushBytes(arena, bytesToAllocate * sizeof(u8));
|
||||
if (!chunk) return NULL;
|
||||
|
||||
StringHeader *header = CAST(StringHeader *) chunk;
|
||||
header->len = len;
|
||||
return &header->string;
|
||||
}
|
||||
#include "Dengine/Math.h"
|
||||
|
||||
i32 common_strlen(const char *const string)
|
||||
{
|
||||
i32 result = 0;
|
||||
while (string[result])
|
||||
result++;
|
||||
while (string[result]) result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -175,7 +12,8 @@ i32 common_strcmp(const char *a, const char *b)
|
||||
{
|
||||
while (*a == *b)
|
||||
{
|
||||
if (!*a) return 0;
|
||||
if (!*a)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
@ -217,8 +55,8 @@ INTERNAL void reverseString(char *const buf, const i32 bufSize)
|
||||
for (i32 i = 0; i < mid; i++)
|
||||
{
|
||||
char tmp = buf[i];
|
||||
buf[i] = buf[(bufSize - 1) - i];
|
||||
buf[(bufSize - 1) - i] = tmp;
|
||||
buf[i] = buf[(bufSize-1) - i];
|
||||
buf[(bufSize-1) - i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,14 +64,9 @@ void common_itoa(i32 value, char *buf, i32 bufSize)
|
||||
{
|
||||
if (!buf || bufSize == 0) return;
|
||||
|
||||
if (value == 0)
|
||||
{
|
||||
buf[0] = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE(doyle): Max 32bit integer (+-)2147483647
|
||||
i32 charIndex = 0;
|
||||
|
||||
b32 negative = FALSE;
|
||||
if (value < 0) negative = TRUE;
|
||||
|
||||
@ -247,16 +80,8 @@ void common_itoa(i32 value, char *buf, i32 bufSize)
|
||||
val /= 10;
|
||||
}
|
||||
|
||||
// NOTE(doyle): If string is negative, we only want to reverse starting
|
||||
// from the second character, so we don't put the negative sign at the end
|
||||
if (negative)
|
||||
{
|
||||
reverseString(buf + 1, charIndex - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
reverseString(buf, charIndex);
|
||||
}
|
||||
// NOTE(doyle): The actual string length may differ from the bufSize
|
||||
reverseString(buf, common_strlen(buf));
|
||||
}
|
||||
|
||||
// TODO(doyle): Consider if we should trash ptrs in string operations in general
|
||||
@ -312,9 +137,9 @@ u32 common_murmurHash2(const void *key, i32 len, u32 seed)
|
||||
|
||||
// Mix 4 bytes at a time into the hash
|
||||
|
||||
const unsigned char *data = (const unsigned char *)key;
|
||||
const unsigned char * data = (const unsigned char *)key;
|
||||
|
||||
while (len >= 4)
|
||||
while(len >= 4)
|
||||
{
|
||||
u32 k = *(u32 *)data;
|
||||
|
||||
@ -331,14 +156,11 @@ u32 common_murmurHash2(const void *key, i32 len, u32 seed)
|
||||
|
||||
// Handle the last few bytes of the input array
|
||||
|
||||
switch (len)
|
||||
switch(len)
|
||||
{
|
||||
case 3:
|
||||
h ^= data[2] << 16;
|
||||
case 2:
|
||||
h ^= data[1] << 8;
|
||||
case 1:
|
||||
h ^= data[0];
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0];
|
||||
h *= m;
|
||||
};
|
||||
|
||||
|
332
src/Debug.c
332
src/Debug.c
@ -1,15 +1,16 @@
|
||||
#include "Dengine/Debug.h"
|
||||
#include "Dengine/AssetManager.h"
|
||||
#include "Dengine/Entity.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
#include "Dengine/Platform.h"
|
||||
#include "Dengine/Renderer.h"
|
||||
#include "Dengine/Asteroid.h"
|
||||
#include "Dengine/WorldTraveller.h"
|
||||
|
||||
typedef struct DebugState
|
||||
{
|
||||
b32 init;
|
||||
Font font;
|
||||
i32 callCount[debugcount_num];
|
||||
i32 *callCount;
|
||||
f32 stringLineGap;
|
||||
|
||||
/* Debug strings rendered in top left corner */
|
||||
@ -28,12 +29,57 @@ typedef struct DebugState
|
||||
|
||||
GLOBAL_VAR DebugState GLOBAL_debug;
|
||||
|
||||
void debug_init(v2 windowSize, Font font)
|
||||
inline char *debug_entitystate_string(i32 val)
|
||||
{
|
||||
char *string;
|
||||
switch(val)
|
||||
{
|
||||
case entitystate_idle:
|
||||
string = "EntityState_Idle";
|
||||
break;
|
||||
case entitystate_battle:
|
||||
string = "EntityState_Battle";
|
||||
break;
|
||||
case entitystate_attack:
|
||||
string = "EntityState_Attack";
|
||||
break;
|
||||
case entitystate_dead:
|
||||
string = "EntityState_Dead";
|
||||
break;
|
||||
case entitystate_count:
|
||||
string = "EntityState_Count (Error!)";
|
||||
break;
|
||||
case entitystate_invalid:
|
||||
string = "EntityState_Invalid (Error!)";
|
||||
break;
|
||||
default:
|
||||
string = "EntityState Unknown (NOT DEFINED)";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
inline char *debug_entityattack_string(i32 val)
|
||||
{
|
||||
char *string;
|
||||
switch(val)
|
||||
{
|
||||
case entityattack_count:
|
||||
string = "EntityAttack_Count (Error!)";
|
||||
break;
|
||||
case entityattack_invalid:
|
||||
string = "EntityAttack_Invalid";
|
||||
break;
|
||||
default:
|
||||
string = "EntityAttack Unknown (NOT DEFINED)";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
void debug_init(MemoryArena_ *arena, v2 windowSize, Font font)
|
||||
{
|
||||
GLOBAL_debug.font = font;
|
||||
|
||||
for (i32 i = 0; i < debugcount_num; i++) GLOBAL_debug.callCount[i] = 0;
|
||||
|
||||
GLOBAL_debug.callCount =
|
||||
memory_pushBytes(arena, debugcount_num * sizeof(i32));
|
||||
GLOBAL_debug.stringLineGap = CAST(f32) font.verticalSpacing;
|
||||
|
||||
/* Init debug string stack */
|
||||
@ -41,7 +87,8 @@ void debug_init(v2 windowSize, Font font)
|
||||
GLOBAL_debug.stringUpdateTimer = 0.0f;
|
||||
GLOBAL_debug.stringUpdateRate = 0.15f;
|
||||
|
||||
GLOBAL_debug.initialStringP = V2(0.0f, (windowSize.h - 1.8f * GLOBAL_debug.stringLineGap));
|
||||
GLOBAL_debug.initialStringP =
|
||||
V2(0.0f, (windowSize.h - 1.8f * GLOBAL_debug.stringLineGap));
|
||||
GLOBAL_debug.currStringP = GLOBAL_debug.initialStringP;
|
||||
|
||||
/* Init gui console */
|
||||
@ -95,6 +142,12 @@ void debug_countIncrement(i32 id)
|
||||
GLOBAL_debug.callCount[id]++;
|
||||
}
|
||||
|
||||
void debug_clearCounter()
|
||||
{
|
||||
for (i32 i = 0; i < debugcount_num; i++)
|
||||
GLOBAL_debug.callCount[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
void debug_consoleLog(char *string, char *file, int lineNum)
|
||||
{
|
||||
@ -222,10 +275,10 @@ INTERNAL void updateAndRenderDebugStack(Renderer *renderer, MemoryArena_ *arena,
|
||||
for (i32 i = 0; i < GLOBAL_debug.numDebugStrings; i++)
|
||||
{
|
||||
f32 rotate = 0;
|
||||
v4 color = V4(1, 1, 1, 1);
|
||||
renderer_stringFixed(
|
||||
renderer, arena, &GLOBAL_debug.font, GLOBAL_debug.debugStrings[i],
|
||||
GLOBAL_debug.currStringP, V2(0, 0), rotate, color, 0, 0);
|
||||
v4 color = V4(0, 0, 0, 1);
|
||||
renderer_staticString(renderer, arena, &GLOBAL_debug.font,
|
||||
GLOBAL_debug.debugStrings[i],
|
||||
GLOBAL_debug.currStringP, V2(0, 0), rotate, color);
|
||||
GLOBAL_debug.currStringP.y -= (0.9f * GLOBAL_debug.stringLineGap);
|
||||
}
|
||||
|
||||
@ -254,51 +307,248 @@ INTERNAL void renderConsole(Renderer *renderer, MemoryArena_ *arena)
|
||||
for (i32 i = 0; i < maxConsoleLines; i++)
|
||||
{
|
||||
f32 rotate = 0;
|
||||
v4 color = V4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
renderer_stringFixed(renderer, arena, &GLOBAL_debug.font,
|
||||
v4 color = V4(0, 0, 0, 1);
|
||||
renderer_staticString(renderer, arena, &GLOBAL_debug.font,
|
||||
GLOBAL_debug.console[i], consoleStrP,
|
||||
V2(0, 0), rotate, color, 0, 0);
|
||||
V2(0, 0), rotate, color);
|
||||
consoleStrP.y -= (0.9f * GLOBAL_debug.stringLineGap);
|
||||
}
|
||||
}
|
||||
|
||||
void debug_drawUi(GameState *state, f32 dt)
|
||||
{
|
||||
{ // Print Memory Arena Info
|
||||
DEBUG_PUSH_STRING("== MEMORY ARENAS ==");
|
||||
MemoryArena_ *transient = &state->transientArena;
|
||||
i32 transientSizeInKbs = transient->size / 1024;
|
||||
i32 transientUsedInKbs = transient->used / 1024;
|
||||
v2 transientUsage = V2i(transientUsedInKbs, transientSizeInKbs);
|
||||
DEBUG_PUSH_VAR("Transient Usage: %.0f/%.0f", transientUsage, "v2");
|
||||
AssetManager *assetManager = &state->assetManager;
|
||||
Renderer *renderer = &state->renderer;
|
||||
World *const world = &state->world[state->currWorldIndex];
|
||||
Entity *hero = &world->entities[entity_getIndex(world, world->heroId)];
|
||||
MemoryArena_ *transientArena = &state->transientArena;
|
||||
|
||||
MemoryArena_ *persistent = &state->persistentArena;
|
||||
i32 persistentSizeInKbs = persistent->size / 1024;
|
||||
i32 persistentUsedInKbs = persistent->used / 1024;
|
||||
v2 persistentUsage = V2i(persistentUsedInKbs, persistentSizeInKbs);
|
||||
DEBUG_PUSH_VAR("Permanent Usage: %.0f/%.0f", persistentUsage, "v2");
|
||||
// TODO(doyle): Dumb copy function from game so we don't expose api
|
||||
Rect camera = {world->cameraPos, renderer->size};
|
||||
// NOTE(doyle): Lock camera if it passes the bounds of the world
|
||||
if (camera.pos.x <= world->bounds.x)
|
||||
camera.pos.x = world->bounds.x;
|
||||
|
||||
GameWorldState *world = ASTEROID_GET_STATE_DATA(state, GameWorldState);
|
||||
if (world)
|
||||
// TODO(doyle): Allow Y panning when we need it
|
||||
f32 cameraTopBoundInPixels = camera.pos.y + camera.size.h;
|
||||
if (cameraTopBoundInPixels >= world->bounds.w)
|
||||
camera.pos.y = (world->bounds.w - camera.size.h);
|
||||
|
||||
f32 cameraRightBoundInPixels = camera.pos.x + camera.size.w;
|
||||
if (cameraRightBoundInPixels >= world->bounds.z)
|
||||
camera.pos.x = (world->bounds.z - camera.size.w);
|
||||
|
||||
if (camera.pos.y <= world->bounds.y) camera.pos.y = world->bounds.y;
|
||||
|
||||
Font *font = &GLOBAL_debug.font;
|
||||
if (world->numEntitiesInBattle > 0)
|
||||
{
|
||||
MemoryArena_ *entityArena = &world->entityArena;
|
||||
i32 entitySizeInKbs = entityArena->size / 1024;
|
||||
i32 entityUsedInKbs = entityArena->used / 1024;
|
||||
v2 entityUsage = V2i(entityUsedInKbs, entitySizeInKbs);
|
||||
DEBUG_PUSH_VAR("Entity Usage: %.0f/%.0f", entityUsage, "v2");
|
||||
DEBUG_PUSH_STRING("== ==");
|
||||
v4 color = V4(1.0f, 0, 0, 1);
|
||||
char *battleStr = "IN-BATTLE RANGE";
|
||||
f32 strLenInPixels =
|
||||
CAST(f32)(font->maxSize.w * common_strlen(battleStr));
|
||||
v2 strPos = V2((renderer->size.w * 0.5f) - (strLenInPixels * 0.5f),
|
||||
renderer->size.h - 300.0f);
|
||||
renderer_staticString(&state->renderer, transientArena, font, battleStr,
|
||||
strPos, V2(0, 0), 0, color);
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < world->freeEntityIndex; i++)
|
||||
{
|
||||
Entity *const entity = &world->entities[i];
|
||||
if (entity->state == entitystate_dead) continue;
|
||||
|
||||
/* Render debug markers on entities */
|
||||
v4 color = V4(1, 1, 1, 1);
|
||||
char *debugString = NULL;
|
||||
switch (entity->type)
|
||||
{
|
||||
case entitytype_mob:
|
||||
color = V4(1, 0, 0, 1);
|
||||
debugString = "MOB";
|
||||
break;
|
||||
|
||||
case entitytype_hero:
|
||||
color = V4(0, 0, 1.0f, 1);
|
||||
debugString = "HERO";
|
||||
break;
|
||||
|
||||
case entitytype_npc:
|
||||
color = V4(0, 1.0f, 0, 1);
|
||||
debugString = "NPC";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (debugString)
|
||||
{
|
||||
v2 strPos = v2_add(entity->pos, entity->hitbox);
|
||||
i32 indexOfLowerAInMetrics = 'a' - CAST(i32) font->codepointRange.x;
|
||||
strPos.y += font->charMetrics[indexOfLowerAInMetrics].offset.y;
|
||||
|
||||
renderer_string(&state->renderer, transientArena, camera, font,
|
||||
debugString, strPos, V2(0, 0), 0, color);
|
||||
|
||||
f32 stringLineGap = 1.1f * font->verticalSpacing;
|
||||
strPos.y -= GLOBAL_debug.stringLineGap;
|
||||
|
||||
char entityPosStr[128];
|
||||
snprintf(entityPosStr, ARRAY_COUNT(entityPosStr), "%06.2f, %06.2f",
|
||||
entity->pos.x, entity->pos.y);
|
||||
renderer_string(&state->renderer, transientArena, camera, font,
|
||||
entityPosStr, strPos, V2(0, 0), 0, color);
|
||||
|
||||
strPos.y -= GLOBAL_debug.stringLineGap;
|
||||
char entityIDStr[32];
|
||||
snprintf(entityIDStr, ARRAY_COUNT(entityIDStr), "ID: %4d/%d", entity->id,
|
||||
world->uniqueIdAccumulator-1);
|
||||
renderer_string(&state->renderer, transientArena, camera, font,
|
||||
entityIDStr, strPos, V2(0, 0), 0, color);
|
||||
|
||||
if (entity->stats)
|
||||
{
|
||||
strPos.y -= GLOBAL_debug.stringLineGap;
|
||||
char entityHealth[32];
|
||||
snprintf(entityHealth, ARRAY_COUNT(entityHealth), "HP: %3.0f/%3.0f",
|
||||
entity->stats->health, entity->stats->maxHealth);
|
||||
renderer_string(&state->renderer, transientArena, camera,
|
||||
font, entityHealth, strPos, V2(0, 0), 0, color);
|
||||
|
||||
strPos.y -= GLOBAL_debug.stringLineGap;
|
||||
char entityTimer[32];
|
||||
snprintf(entityTimer, ARRAY_COUNT(entityTimer), "ATB: %3.0f/%3.0f",
|
||||
entity->stats->actionTimer, entity->stats->actionRate);
|
||||
renderer_string(&state->renderer, transientArena, camera,
|
||||
font, entityTimer, strPos, V2(0, 0), 0, color);
|
||||
|
||||
strPos.y -= GLOBAL_debug.stringLineGap;
|
||||
char entityIdTarget[32];
|
||||
snprintf(entityIdTarget, ARRAY_COUNT(entityIdTarget),
|
||||
"Targetting ID: %d", entity->stats->entityIdToAttack);
|
||||
renderer_string(&state->renderer, transientArena, camera,
|
||||
font, entityIdTarget, strPos, V2(0, 0), 0, color);
|
||||
}
|
||||
|
||||
strPos.y -= GLOBAL_debug.stringLineGap;
|
||||
char *entityStateStr = debug_entitystate_string(entity->state);
|
||||
renderer_string(&state->renderer, transientArena, camera, font,
|
||||
entityStateStr, strPos, V2(0, 0), 0, color);
|
||||
|
||||
if (entity->audioRenderer)
|
||||
{
|
||||
strPos.y -= GLOBAL_debug.stringLineGap;
|
||||
char entityAudioSourceIndex[32];
|
||||
snprintf(entityAudioSourceIndex,
|
||||
ARRAY_COUNT(entityAudioSourceIndex),
|
||||
"AudioSource Index: %d",
|
||||
entity->audioRenderer->sourceIndex);
|
||||
renderer_string(&state->renderer, transientArena, camera,
|
||||
font, entityAudioSourceIndex, strPos, V2(0, 0),
|
||||
0, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_PUSH_VAR("Num RenderGroups: %d", &state->renderer.groupsInUse,
|
||||
/* Render debug info stack */
|
||||
DEBUG_PUSH_STRING("== Controls ==");
|
||||
DEBUG_PUSH_STRING(" [: Spawn a mob");
|
||||
DEBUG_PUSH_STRING("<TAB>: Switch UI element");
|
||||
DEBUG_PUSH_STRING("<ESC>: Close program");
|
||||
DEBUG_PUSH_STRING("");
|
||||
|
||||
DEBUG_PUSH_STRING("== Config == ");
|
||||
DEBUG_PUSH_VAR("Toggle World Audio: %d", state->config.playWorldAudio,
|
||||
"i32");
|
||||
DEBUG_PUSH_VAR("Num Vertex: %d",
|
||||
GLOBAL_debug.callCount[debugcount_numVertex], "i32");
|
||||
DEBUG_PUSH_VAR("Toggle Debug Display: %d", state->config.showDebugDisplay,
|
||||
"i32");
|
||||
DEBUG_PUSH_STRING("");
|
||||
|
||||
updateAndRenderDebugStack(&state->renderer, &state->transientArena, dt);
|
||||
renderConsole(&state->renderer, &state->transientArena);
|
||||
DEBUG_PUSH_STRING("== Hero Properties == ");
|
||||
DEBUG_PUSH_VAR("Hero Pos: %06.2f, %06.2f", hero->pos, "v2");
|
||||
DEBUG_PUSH_VAR("Hero dPos: %06.2f, %06.2f", hero->dPos, "v2");
|
||||
DEBUG_PUSH_VAR("Hero Busy Duration: %05.3f", hero->stats->busyDuration, "f32");
|
||||
char *heroStateString = debug_entitystate_string(hero->state);
|
||||
DEBUG_PUSH_VAR("Hero State: %s", *heroStateString, "char");
|
||||
char *heroQueuedAttackStr =
|
||||
debug_entityattack_string(hero->stats->queuedAttack);
|
||||
DEBUG_PUSH_VAR("Hero QueuedAttack: %s", *heroQueuedAttackStr, "char");
|
||||
DEBUG_PUSH_STRING("");
|
||||
|
||||
{ // Clear debug call counters
|
||||
for (i32 i = 0; i < debugcount_num; i++) GLOBAL_debug.callCount[i] = 0;
|
||||
DEBUG_PUSH_STRING("== State Properties == ");
|
||||
DEBUG_PUSH_VAR("FreeEntityIndex: %d", world->freeEntityIndex, "i32");
|
||||
DEBUG_PUSH_VAR("GLDrawArray Calls: %d",
|
||||
GLOBAL_debug.callCount[debugcount_drawArrays], "i32");
|
||||
DEBUG_PUSH_VAR("PlatformMemAlloc Calls: %d",
|
||||
GLOBAL_debug.callCount[debugcount_platformMemAlloc], "i32");
|
||||
DEBUG_PUSH_VAR("PlatformMemFree Calls: %d",
|
||||
GLOBAL_debug.callCount[debugcount_platformMemFree], "i32");
|
||||
|
||||
i32 vertexesUsed = GLOBAL_debug.callCount[debugcount_numVertex];
|
||||
i32 vertexesAvail =
|
||||
(ARRAY_COUNT(state->renderer.groups) * state->renderer.groupCapacity);
|
||||
i32 vertexesLeft = vertexesAvail - vertexesUsed;
|
||||
v2 vertexData = V2i(vertexesUsed, vertexesAvail);
|
||||
DEBUG_PUSH_VAR("Vertexes Rendered: %1.0f/%1.0f", vertexData, "v2");
|
||||
DEBUG_PUSH_VAR("Vertexes Left: %d", vertexesLeft, "i32");
|
||||
|
||||
i32 groupsUsed = GLOBAL_debug.callCount[debugcount_renderGroups];
|
||||
i32 groupsAvail = ARRAY_COUNT(state->renderer.groups);
|
||||
v2 groupData = V2i(groupsUsed, groupsAvail);
|
||||
DEBUG_PUSH_VAR("Render Groups Used: %1.0f/%1.0f", groupData, "v2");
|
||||
|
||||
DEBUG_PUSH_VAR("Mouse Pos: %06.2f, %06.2f", state->input.mouseP, "v2");
|
||||
|
||||
/*
|
||||
*****************
|
||||
* MEMORY DISPLAY
|
||||
*****************
|
||||
*/
|
||||
i32 debug_bAllocated = transientArena->used;
|
||||
i32 debug_kbAllocated = debug_bAllocated / 1024;
|
||||
i32 debug_mbAllocated = debug_kbAllocated / 1024;
|
||||
DEBUG_PUSH_VAR("TransientArena Used: %db", debug_bAllocated, "i32");
|
||||
DEBUG_PUSH_VAR("TransientArena Used: %dkb", debug_kbAllocated, "i32");
|
||||
DEBUG_PUSH_VAR("TransientArena Used: %dmb", debug_mbAllocated, "i32");
|
||||
DEBUG_PUSH_STRING("");
|
||||
|
||||
debug_bAllocated = state->arena_.used;
|
||||
debug_kbAllocated = debug_bAllocated / 1024;
|
||||
debug_mbAllocated = debug_kbAllocated / 1024;
|
||||
DEBUG_PUSH_VAR("PersistentArena Used: %db", debug_bAllocated, "i32");
|
||||
DEBUG_PUSH_VAR("PersistentArena Used: %dkb", debug_kbAllocated, "i32");
|
||||
DEBUG_PUSH_VAR("PersistentArena Used: %dmb", debug_mbAllocated, "i32");
|
||||
DEBUG_PUSH_STRING("");
|
||||
|
||||
AudioManager *audioManager = &state->audioManager;
|
||||
DEBUG_PUSH_STRING("== Audio System ==");
|
||||
for (i32 i = 0; i < ARRAY_COUNT(audioManager->sourceList); i++)
|
||||
{
|
||||
if (audioManager->sourceList[i].isFree) continue;
|
||||
v3 tmp = V3i(i, audioManager->sourceList[i].id,
|
||||
audioManager->sourceList[i].isFree);
|
||||
DEBUG_PUSH_VAR("Source ID[%02.0f].id[%02.0f].isFree: %1.0f", tmp, "v3");
|
||||
}
|
||||
DEBUG_PUSH_STRING("");
|
||||
|
||||
DEBUG_PUSH_STRING("== EntityIDs in Battle List == ");
|
||||
DEBUG_PUSH_VAR("NumEntitiesInBattle: %d", world->numEntitiesInBattle,
|
||||
"i32");
|
||||
if (world->numEntitiesInBattle > 0)
|
||||
{
|
||||
for (i32 i = 0; i < world->maxEntities; i++)
|
||||
{
|
||||
if (world->entityIdInBattle[i])
|
||||
DEBUG_PUSH_VAR("Entity ID: %d", i, "i32");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PUSH_STRING("-none-");
|
||||
}
|
||||
|
||||
updateAndRenderDebugStack(&state->renderer, transientArena, dt);
|
||||
renderConsole(&state->renderer, transientArena);
|
||||
debug_clearCounter();
|
||||
}
|
||||
|
170
src/Entity.c
170
src/Entity.c
@ -1,23 +1,24 @@
|
||||
#include "Dengine/Entity.h"
|
||||
#include "Dengine/AssetManager.h"
|
||||
#include "Dengine/Debug.h"
|
||||
#include "Dengine/Math.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
#include "Dengine/WorldTraveller.h"
|
||||
|
||||
SubTexture entity_subTexGetCurr(Entity *const entity)
|
||||
SubTexture entity_getActiveSubTexture(Entity *const entity)
|
||||
{
|
||||
EntityAnim *entityAnim = &entity->animList[entity->animListIndex];
|
||||
EntityAnim *entityAnim = &entity->animList[entity->currAnimId];
|
||||
Animation *anim = entityAnim->anim;
|
||||
char *frameName = anim->frameList[entityAnim->currFrame];
|
||||
|
||||
SubTexture result = asset_atlasGetSubTex(anim->atlas, frameName);
|
||||
SubTexture result = asset_getAtlasSubTex(anim->atlas, frameName);
|
||||
return result;
|
||||
}
|
||||
|
||||
void entity_animSet(Entity *const entity, const char *const animName)
|
||||
void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity,
|
||||
const char *const animName)
|
||||
{
|
||||
/* Reset current anim data */
|
||||
EntityAnim *currEntityAnim = &entity->animList[entity->animListIndex];
|
||||
EntityAnim *currEntityAnim = &entity->animList[entity->currAnimId];
|
||||
currEntityAnim->currDuration = currEntityAnim->anim->frameDuration;
|
||||
currEntityAnim->currFrame = 0;
|
||||
currEntityAnim->timesCompleted = 0;
|
||||
@ -31,11 +32,14 @@ void entity_animSet(Entity *const entity, const char *const animName)
|
||||
// TODO(doyle): Linear search, but not a problem if list is small
|
||||
if (common_strcmp(anim->key, animName) == 0)
|
||||
{
|
||||
entity->animListIndex = i;
|
||||
entity->currAnimId = i;
|
||||
EntityAnim *newEntityAnim = &entity->animList[i];
|
||||
newEntityAnim->currDuration =
|
||||
newEntityAnim->anim->frameDuration;
|
||||
newEntityAnim->currFrame = 0;
|
||||
|
||||
worldTraveller_registerEvent(eventQueue, eventtype_start_anim,
|
||||
newEntityAnim);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -44,12 +48,13 @@ void entity_animSet(Entity *const entity, const char *const animName)
|
||||
DEBUG_LOG("Entity does not have access to desired anim");
|
||||
}
|
||||
|
||||
void entity_animUpdate(Entity *const entity, const f32 dt)
|
||||
void entity_updateAnim(EventQueue *eventQueue, Entity *const entity,
|
||||
const f32 dt)
|
||||
{
|
||||
if (!entity->tex)
|
||||
return;
|
||||
|
||||
EntityAnim *currEntityAnim = &entity->animList[entity->animListIndex];
|
||||
EntityAnim *currEntityAnim = &entity->animList[entity->currAnimId];
|
||||
Animation *anim = currEntityAnim->anim;
|
||||
|
||||
currEntityAnim->currDuration -= dt;
|
||||
@ -59,18 +64,33 @@ void entity_animUpdate(Entity *const entity, const f32 dt)
|
||||
currEntityAnim->currFrame = currEntityAnim->currFrame % anim->numFrames;
|
||||
if (currEntityAnim->currFrame == 0)
|
||||
{
|
||||
worldTraveller_registerEvent(eventQueue, eventtype_end_anim,
|
||||
currEntityAnim);
|
||||
currEntityAnim->timesCompleted++;
|
||||
}
|
||||
|
||||
currEntityAnim->currDuration = anim->frameDuration;
|
||||
}
|
||||
|
||||
// NOTE(doyle): If humanoid entity, let animation dictate render size which
|
||||
// may exceed the hitbox size of the entity
|
||||
switch (entity->type)
|
||||
{
|
||||
case entitytype_hero:
|
||||
case entitytype_mob:
|
||||
case entitytype_npc:
|
||||
case entitytype_weapon:
|
||||
case entitytype_projectile:
|
||||
char *frameName = anim->frameList[currEntityAnim->currFrame];
|
||||
SubTexture texRect = asset_atlasGetSubTex(anim->atlas, frameName);
|
||||
entity->size = v2_scale(texRect.rect.max, entity->scale);
|
||||
SubTexture texRect =
|
||||
asset_getAtlasSubTex(anim->atlas, frameName);
|
||||
entity->size = v2_scale(texRect.rect.size, entity->scale);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void entity_animAdd(AssetManager *const assetManager, Entity *const entity,
|
||||
void entity_addAnim(AssetManager *const assetManager, Entity *const entity,
|
||||
const char *const animName)
|
||||
{
|
||||
i32 freeAnimIndex = 0;
|
||||
@ -79,7 +99,7 @@ void entity_animAdd(AssetManager *const assetManager, Entity *const entity,
|
||||
EntityAnim *entityAnim = &entity->animList[i];
|
||||
if (!entityAnim->anim)
|
||||
{
|
||||
entityAnim->anim = asset_animGet(assetManager, animName);
|
||||
entityAnim->anim = asset_getAnim(assetManager, animName);
|
||||
entityAnim->currFrame = 0;
|
||||
entityAnim->currDuration = entityAnim->anim->frameDuration;
|
||||
return;
|
||||
@ -89,26 +109,126 @@ void entity_animAdd(AssetManager *const assetManager, Entity *const entity,
|
||||
DEBUG_LOG("No more free entity animation slots");
|
||||
}
|
||||
|
||||
v2 *entity_generateUpdatedVertexList(MemoryArena_ *transientArena,
|
||||
Entity *entity)
|
||||
Entity *const entity_add(MemoryArena_ *const arena, World *const world,
|
||||
const v2 pos, const v2 size, const f32 scale,
|
||||
const enum EntityType type,
|
||||
const enum Direction direction, Texture *const tex,
|
||||
const b32 collides)
|
||||
{
|
||||
ASSERT(entity->vertexPoints);
|
||||
ASSERT(entity->numVertexPoints >= 3);
|
||||
|
||||
v2 *result =
|
||||
memory_pushBytes(transientArena, entity->numVertexPoints * sizeof(v2));
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(world);
|
||||
ASSERT(world->freeEntityIndex < world->maxEntities);
|
||||
ASSERT(type < entitytype_count);
|
||||
#endif
|
||||
|
||||
for (i32 i = 0; i < entity->numVertexPoints; i++)
|
||||
Entity entity = {0};
|
||||
entity.id = world->uniqueIdAccumulator++;
|
||||
entity.pos = pos;
|
||||
entity.size = size;
|
||||
entity.hitbox = size;
|
||||
entity.scale = scale;
|
||||
entity.type = type;
|
||||
entity.direction = direction;
|
||||
entity.tex = tex;
|
||||
entity.collides = collides;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
result[i] = v2_add(entity->vertexPoints[i], entity->offset);
|
||||
result[i] = v2_add(result[i], entity->pos);
|
||||
case entitytype_hero:
|
||||
entity.stats = MEMORY_PUSH_STRUCT(arena, EntityStats);
|
||||
entity.stats->maxHealth = 100;
|
||||
entity.stats->health = entity.stats->maxHealth;
|
||||
entity.stats->actionRate = 100;
|
||||
entity.stats->actionTimer = entity.stats->actionRate;
|
||||
entity.stats->actionSpdMul = 100;
|
||||
entity.stats->entityIdToAttack = -1;
|
||||
entity.stats->queuedAttack = entityattack_invalid;
|
||||
entity.state = entitystate_idle;
|
||||
entity.collidesWith[entitytype_mob] = TRUE;
|
||||
break;
|
||||
case entitytype_mob:
|
||||
{
|
||||
entity.stats = MEMORY_PUSH_STRUCT(arena, EntityStats);
|
||||
entity.stats->maxHealth = 100;
|
||||
entity.stats->health = entity.stats->maxHealth;
|
||||
entity.stats->actionRate = 80;
|
||||
entity.stats->actionTimer = entity.stats->actionRate;
|
||||
entity.stats->actionSpdMul = 100;
|
||||
entity.stats->entityIdToAttack = -1;
|
||||
entity.stats->queuedAttack = entityattack_invalid;
|
||||
entity.state = entitystate_idle;
|
||||
entity.collidesWith[entitytype_hero] = TRUE;
|
||||
break;
|
||||
}
|
||||
case entitytype_projectile:
|
||||
case entitytype_attackObject:
|
||||
entity.stats = MEMORY_PUSH_STRUCT(arena, EntityStats);
|
||||
entity.stats->maxHealth = 100;
|
||||
entity.stats->health = entity.stats->maxHealth;
|
||||
entity.stats->actionRate = 100;
|
||||
entity.stats->actionTimer = entity.stats->actionRate;
|
||||
entity.stats->actionSpdMul = 100;
|
||||
entity.stats->entityIdToAttack = -1;
|
||||
entity.stats->queuedAttack = entityattack_invalid;
|
||||
entity.state = entitystate_idle;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
math_applyRotationToVertexes(entity->pos, V2(0 ,0),
|
||||
DEGREES_TO_RADIANS(entity->rotation), result,
|
||||
entity->numVertexPoints);
|
||||
world->entities[world->freeEntityIndex++] = entity;
|
||||
Entity *result = &world->entities[world->freeEntityIndex - 1];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void entity_clearData(MemoryArena_ *const arena, World *const world,
|
||||
Entity *const entity)
|
||||
{
|
||||
// TODO(doyle): Mem free// memory leak!!
|
||||
|
||||
/*
|
||||
if (entity->stats)
|
||||
PLATFORM_MEM_FREE(arena, entity->stats, sizeof(EntityStats));
|
||||
|
||||
if (entity->audioRenderer)
|
||||
PLATFORM_MEM_FREE(arena, entity->audioRenderer,
|
||||
sizeof(AudioRenderer) * entity->numAudioRenderers);
|
||||
*/
|
||||
|
||||
entity->type = entitytype_null;
|
||||
}
|
||||
|
||||
i32 entity_getIndex(World *const world, const i32 entityId)
|
||||
{
|
||||
i32 first = 0;
|
||||
i32 last = world->freeEntityIndex - 1;
|
||||
|
||||
while (first <= last)
|
||||
{
|
||||
i32 middle = (first + last) / 2;
|
||||
|
||||
if (world->entities[middle].id > entityId)
|
||||
last = middle - 1;
|
||||
else if (world->entities[middle].id < entityId)
|
||||
first = middle + 1;
|
||||
else
|
||||
return middle;
|
||||
}
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
Entity *entity_get(World *const world, const i32 entityId)
|
||||
{
|
||||
Entity *result = NULL;
|
||||
i32 worldIndex = entity_getIndex(world, entityId);
|
||||
if (worldIndex != -1) result = &world->entities[worldIndex];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -4,28 +4,5 @@ void memory_arenaInit(MemoryArena_ *arena, void *base, size_t size)
|
||||
{
|
||||
arena->size = size;
|
||||
arena->used = 0;
|
||||
arena->base = CAST(u8 *) base;
|
||||
arena->tempMemoryCount = 0;
|
||||
}
|
||||
|
||||
TempMemory memory_beginTempRegion(MemoryArena_ *arena)
|
||||
{
|
||||
TempMemory result = {0};
|
||||
result.arena = arena;
|
||||
result.used = arena->used;
|
||||
|
||||
arena->tempMemoryCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void memory_endTempRegion(TempMemory tempMemory)
|
||||
{
|
||||
MemoryArena_ *arena = tempMemory.arena;
|
||||
ASSERT(arena->used > tempMemory.used)
|
||||
|
||||
arena->used = tempMemory.used;
|
||||
ASSERT(arena->tempMemoryCount > 0)
|
||||
|
||||
arena->tempMemoryCount--;
|
||||
arena->base = CAST(u8 *)base;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "Dengine/Debug.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
|
||||
void platform_memoryFree(MemoryArena_ *arena, void *data, size_t numBytes)
|
||||
void platform_memoryFree(MemoryArena_ *arena, void *data, i32 numBytes)
|
||||
{
|
||||
if (data) free(data);
|
||||
|
||||
@ -16,7 +16,7 @@ void platform_memoryFree(MemoryArena_ *arena, void *data, size_t numBytes)
|
||||
#endif
|
||||
}
|
||||
|
||||
void *platform_memoryAlloc(MemoryArena_ *arena, size_t numBytes)
|
||||
void *platform_memoryAlloc(MemoryArena_ *arena, i32 numBytes)
|
||||
{
|
||||
void *result = calloc(1, numBytes);
|
||||
|
||||
@ -28,7 +28,6 @@ void *platform_memoryAlloc(MemoryArena_ *arena, size_t numBytes)
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO(doyle): If we use arena temporary memory this is not necessary
|
||||
void platform_closeFileRead(MemoryArena_ *arena, PlatformFileRead *file)
|
||||
{
|
||||
// TODO(doyle): Mem free
|
||||
@ -95,87 +94,3 @@ i32 platform_readFileToBuffer(MemoryArena_ *arena, const char *const filePath,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void platform_inputBufferProcess(InputBuffer *inputBuffer, f32 dt)
|
||||
{
|
||||
KeyState *keyBuffer = inputBuffer->keys;
|
||||
for (enum KeyCode code = 0; code < keycode_count; code++)
|
||||
{
|
||||
KeyState *key = &keyBuffer[code];
|
||||
|
||||
u32 halfTransitionCount =
|
||||
key->newHalfTransitionCount - key->oldHalfTransitionCount;
|
||||
|
||||
if (halfTransitionCount > 0)
|
||||
{
|
||||
b32 transitionCountIsOdd = ((halfTransitionCount & 1) == 1);
|
||||
if (transitionCountIsOdd)
|
||||
{
|
||||
/* If it was not last ended down, then update interval if
|
||||
* necessary */
|
||||
if (!common_isSet(key->flags, keystateflag_ended_down))
|
||||
{
|
||||
if (key->delayInterval > 0) key->delayInterval -= dt;
|
||||
|
||||
key->flags |= keystateflag_pressed_on_curr_frame;
|
||||
}
|
||||
key->flags ^= keystateflag_ended_down;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
key->flags &= (~keystateflag_pressed_on_curr_frame);
|
||||
}
|
||||
|
||||
key->newHalfTransitionCount = key->oldHalfTransitionCount;
|
||||
}
|
||||
}
|
||||
|
||||
b32 platform_queryKey(KeyState *key, enum ReadKeyType readType,
|
||||
f32 delayInterval)
|
||||
{
|
||||
|
||||
if (!common_isSet(key->flags, keystateflag_ended_down)) return FALSE;
|
||||
|
||||
switch (readType)
|
||||
{
|
||||
case readkeytype_one_shot:
|
||||
{
|
||||
if (common_isSet(key->flags, keystateflag_pressed_on_curr_frame))
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case readkeytype_repeat:
|
||||
case readkeytype_delay_repeat:
|
||||
{
|
||||
if (common_isSet(key->flags, keystateflag_pressed_on_curr_frame))
|
||||
{
|
||||
if (readType == readkeytype_delay_repeat)
|
||||
{
|
||||
// TODO(doyle): Let user set arbitrary delay after initial input
|
||||
key->delayInterval = 2 * delayInterval;
|
||||
}
|
||||
else
|
||||
{
|
||||
key->delayInterval = delayInterval;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
else if (key->delayInterval <= 0.0f)
|
||||
{
|
||||
key->delayInterval = delayInterval;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
777
src/Renderer.c
777
src/Renderer.c
@ -5,188 +5,18 @@
|
||||
#include "Dengine/Entity.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
#include "Dengine/OpenGL.h"
|
||||
#include "Dengine/Shader.h"
|
||||
#include "Dengine/Texture.h"
|
||||
|
||||
INTERNAL void shaderUniformSet1i(u32 shaderId, const GLchar *name,
|
||||
const GLuint data)
|
||||
#define RENDER_BOUNDING_BOX FALSE
|
||||
|
||||
INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color,
|
||||
Vertex *vertexList, i32 numVertexes)
|
||||
{
|
||||
GLint uniformLoc = glGetUniformLocation(shaderId, name);
|
||||
glUniform1i(uniformLoc, data);
|
||||
}
|
||||
|
||||
INTERNAL void shaderUniformSetMat4fv(u32 shaderId, const GLchar *name,
|
||||
mat4 data)
|
||||
{
|
||||
GLint uniformLoc = glGetUniformLocation(shaderId, name);
|
||||
GL_CHECK_ERROR();
|
||||
glUniformMatrix4fv(uniformLoc, 1, GL_FALSE, data.e[0]);
|
||||
GL_CHECK_ERROR();
|
||||
}
|
||||
|
||||
INTERNAL void shaderUniformSetVec4f(u32 shaderId, const GLchar *name,
|
||||
v4 data)
|
||||
{
|
||||
GLint uniformLoc = glGetUniformLocation(shaderId, name);
|
||||
glUniform4f(uniformLoc, data.e[0], data.e[1], data.e[2], data.e[3]);
|
||||
}
|
||||
|
||||
|
||||
INTERNAL void shaderUse(u32 shaderId) { glUseProgram(shaderId); }
|
||||
|
||||
void renderer_updateSize(Renderer *renderer, AssetManager *assetManager, v2 windowSize)
|
||||
{
|
||||
renderer->size = windowSize;
|
||||
// renderer->displayScale =
|
||||
// V2(windowSize.x * 1.0f / renderer->referenceScale.x,
|
||||
// windowSize.y * 1.0f / renderer->referenceScale.y);
|
||||
|
||||
// NOTE(doyle): Value to map a screen coordinate to NDC coordinate
|
||||
renderer->vertexNdcFactor =
|
||||
V2(1.0f / renderer->size.w, 1.0f / renderer->size.h);
|
||||
renderer->groupIndexForVertexBatch = -1;
|
||||
|
||||
const mat4 projection =
|
||||
mat4_ortho(0.0f, renderer->size.w, 0.0f, renderer->size.h, 0.0f, 1.0f);
|
||||
|
||||
for (i32 i = 0; i < shaderlist_count; i++)
|
||||
{
|
||||
renderer->shaderList[i] = asset_shaderGet(assetManager, i);
|
||||
shaderUse(renderer->shaderList[i]);
|
||||
shaderUniformSetMat4fv(renderer->shaderList[i], "projection",
|
||||
projection);
|
||||
GL_CHECK_ERROR();
|
||||
}
|
||||
|
||||
renderer->activeShaderId = renderer->shaderList[shaderlist_default];
|
||||
GL_CHECK_ERROR();
|
||||
}
|
||||
|
||||
void renderer_init(Renderer *renderer, AssetManager *assetManager,
|
||||
MemoryArena_ *persistentArena, v2 windowSize)
|
||||
{
|
||||
renderer->referenceScale = V2(1280, 720);
|
||||
renderer_updateSize(renderer, assetManager, windowSize);
|
||||
|
||||
/* Create buffers */
|
||||
glGenVertexArrays(ARRAY_COUNT(renderer->vao), renderer->vao);
|
||||
glGenBuffers(ARRAY_COUNT(renderer->vbo), renderer->vbo);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
// Bind buffers and configure vao, vao automatically intercepts
|
||||
// glBindCalls and associates the state with that buffer for us
|
||||
for (enum RenderMode mode = 0; mode < rendermode_count; mode++)
|
||||
{
|
||||
glBindVertexArray(renderer->vao[mode]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[mode]);
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
u32 numVertexElements = 4;
|
||||
u32 stride = sizeof(RenderVertex);
|
||||
|
||||
glVertexAttribPointer(0, numVertexElements, GL_FLOAT,
|
||||
GL_FALSE, stride, (GLvoid *)0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
/* Unbind */
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
// TODO(doyle): Lazy allocate render group capacity
|
||||
renderer->groupCapacity = 4096;
|
||||
for (i32 i = 0; i < ARRAY_COUNT(renderer->groups); i++)
|
||||
{
|
||||
renderer->groups[i].vertexList = memory_pushBytes(
|
||||
persistentArena, renderer->groupCapacity * sizeof(RenderVertex));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct RenderQuad
|
||||
{
|
||||
RenderVertex vertexList[4];
|
||||
} RenderQuad;
|
||||
|
||||
// NOTE(doyle): A vertex batch is the batch of vertexes comprised to make one
|
||||
// shape
|
||||
INTERNAL void beginVertexBatch(Renderer *renderer)
|
||||
{
|
||||
ASSERT(renderer->vertexBatchState == vertexbatchstate_off);
|
||||
ASSERT(renderer->groupIndexForVertexBatch == -1);
|
||||
renderer->vertexBatchState = vertexbatchstate_initial_add;
|
||||
}
|
||||
|
||||
/*
|
||||
NOTE(doyle): Entity rendering is always done in two pairs of
|
||||
triangles, i.e. quad. To batch render quads as a triangle strip, we
|
||||
need to create zero-area triangles which OGL will omit from
|
||||
rendering.
|
||||
|
||||
The implementation is recognising if the rendered
|
||||
entity is the first in its render group, then we don't need to init
|
||||
a degenerate vertex, and only at the end of its vertex list. But on
|
||||
subsequent renders, we need a degenerate vertex at the front to
|
||||
create the zero-area triangle strip.
|
||||
*/
|
||||
INTERNAL void endVertexBatch(Renderer *renderer)
|
||||
{
|
||||
ASSERT(renderer->vertexBatchState != vertexbatchstate_off);
|
||||
ASSERT(renderer->groupIndexForVertexBatch != -1);
|
||||
|
||||
i32 numDegenerateVertexes = 1;
|
||||
RenderGroup *group = &renderer->groups[renderer->groupIndexForVertexBatch];
|
||||
|
||||
i32 freeVertexSlots = renderer->groupCapacity - group->vertexIndex;
|
||||
if (numDegenerateVertexes < freeVertexSlots)
|
||||
{
|
||||
RenderVertex degenerateVertex =
|
||||
group->vertexList[group->vertexIndex - 1];
|
||||
group->vertexList[group->vertexIndex++] = degenerateVertex;
|
||||
}
|
||||
|
||||
renderer->vertexBatchState = vertexbatchstate_off;
|
||||
renderer->groupIndexForVertexBatch = -1;
|
||||
}
|
||||
|
||||
INTERNAL void applyRotationToVertexes(v2 pos, v2 pivotPoint, Radians rotate,
|
||||
RenderVertex *vertexList,
|
||||
i32 vertexListSize)
|
||||
{
|
||||
if (rotate == 0) return;
|
||||
// NOTE(doyle): Move the world origin to the base position of the object.
|
||||
// Then move the origin to the pivot point (e.g. center of object) and
|
||||
// rotate from that point.
|
||||
v2 pointOfRotation = v2_add(pivotPoint, pos);
|
||||
|
||||
mat4 rotateMat = mat4_translate(pointOfRotation.x, pointOfRotation.y, 0.0f);
|
||||
rotateMat = mat4_mul(rotateMat, mat4_rotate(rotate, 0.0f, 0.0f, 1.0f));
|
||||
rotateMat = mat4_mul(rotateMat, mat4_translate(-pointOfRotation.x,
|
||||
-pointOfRotation.y, 0.0f));
|
||||
for (i32 i = 0; i < vertexListSize; i++)
|
||||
{
|
||||
// NOTE(doyle): Manual matrix multiplication since vertex pos is 2D and
|
||||
// matrix is 4D
|
||||
v2 oldP = vertexList[i].pos;
|
||||
v2 newP = {0};
|
||||
|
||||
newP.x = (oldP.x * rotateMat.e[0][0]) + (oldP.y * rotateMat.e[1][0]) +
|
||||
(rotateMat.e[3][0]);
|
||||
newP.y = (oldP.x * rotateMat.e[0][1]) + (oldP.y * rotateMat.e[1][1]) +
|
||||
(rotateMat.e[3][1]);
|
||||
|
||||
vertexList[i].pos = newP;
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL void addVertexToRenderGroup_(Renderer *renderer, Texture *tex,
|
||||
v4 color, i32 zDepth,
|
||||
RenderVertex *vertexList, i32 numVertexes,
|
||||
enum RenderMode targetRenderMode,
|
||||
RenderFlags flags)
|
||||
{
|
||||
ASSERT(renderer->vertexBatchState != vertexbatchstate_off);
|
||||
ASSERT(numVertexes > 0);
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(numVertexes > 0);
|
||||
|
||||
for (i32 i = 0; i < numVertexes; i++)
|
||||
debug_countIncrement(debugcount_numVertex);
|
||||
#endif
|
||||
@ -197,50 +27,35 @@ INTERNAL void addVertexToRenderGroup_(Renderer *renderer, Texture *tex,
|
||||
{
|
||||
RenderGroup *group = &renderer->groups[i];
|
||||
b32 groupIsValid = FALSE;
|
||||
if (group->init)
|
||||
if (group->tex)
|
||||
{
|
||||
/* If the textures match and have the same color modulation, we can
|
||||
* add these vertices to the current group */
|
||||
|
||||
if (!(group->mode == targetRenderMode)) continue;
|
||||
if (!(v4_equals(group->color, color))) continue;
|
||||
if (!(group->flags == flags)) continue;
|
||||
if (!(group->zDepth == zDepth)) continue;
|
||||
if (!tex && group->tex) continue;
|
||||
|
||||
if (tex && group->tex)
|
||||
if (group->tex->id == tex->id &&
|
||||
v4_equals(group->color, color))
|
||||
{
|
||||
if (!(group->tex->id == tex->id)) continue;
|
||||
}
|
||||
|
||||
groupIsValid = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* New group, unused so initialise it */
|
||||
groupIsValid = TRUE;
|
||||
|
||||
group->init = TRUE;
|
||||
// NOTE(doyle): Mark first vertex as degenerate vertex
|
||||
group->vertexIndex++;
|
||||
group->tex = tex;
|
||||
group->color = color;
|
||||
group->mode = targetRenderMode;
|
||||
group->flags = flags;
|
||||
group->zDepth = zDepth;
|
||||
|
||||
renderer->groupsInUse++;
|
||||
#ifdef DENGINE_DEBUG
|
||||
debug_countIncrement(debugcount_renderGroups);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (groupIsValid)
|
||||
{
|
||||
i32 freeVertexSlots = renderer->groupCapacity - group->vertexIndex;
|
||||
|
||||
// NOTE(doyle): Two at start, two at end
|
||||
i32 numDegenerateVertexes = 0;
|
||||
|
||||
if (renderer->vertexBatchState == vertexbatchstate_initial_add)
|
||||
numDegenerateVertexes = 1;
|
||||
|
||||
if ((numDegenerateVertexes + numVertexes) < freeVertexSlots)
|
||||
if (numVertexes < freeVertexSlots)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
@ -257,46 +72,6 @@ INTERNAL void addVertexToRenderGroup_(Renderer *renderer, Texture *tex,
|
||||
/* Valid group, add to the render group for rendering */
|
||||
if (targetGroup)
|
||||
{
|
||||
// NOTE(doyle): If we are adding 3 vertexes, then we are adding a
|
||||
// triangle to the triangle strip. If so, then depending on which "n-th"
|
||||
// triangle it is we're adding, the winding order in a t-strip
|
||||
// alternates with each triangle (including degenerates). Hence we track
|
||||
// so we know the last winding order in the group.
|
||||
|
||||
// For this to work, we must ensure all incoming vertexes are winding in
|
||||
// ccw order initially. There is also the presumption that, other
|
||||
// rendering methods, such as the quad, consists of an even number of
|
||||
// triangles such that the winding order gets alternated back to the
|
||||
// same order it started with.
|
||||
if (numVertexes == 3)
|
||||
{
|
||||
if (targetGroup->clockwiseWinding)
|
||||
{
|
||||
RenderVertex tmp = vertexList[0];
|
||||
vertexList[0] = vertexList[2];
|
||||
vertexList[2] = tmp;
|
||||
}
|
||||
|
||||
targetGroup->clockwiseWinding =
|
||||
(targetGroup->clockwiseWinding) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
if (renderer->vertexBatchState == vertexbatchstate_initial_add)
|
||||
{
|
||||
if (targetGroup->vertexIndex != 0)
|
||||
{
|
||||
targetGroup->vertexList[targetGroup->vertexIndex++] =
|
||||
vertexList[0];
|
||||
}
|
||||
renderer->vertexBatchState = vertexbatchstate_active;
|
||||
|
||||
// NOTE(doyle): We swap groups to the front if it is valid, so
|
||||
// target group should always be 0
|
||||
ASSERT(renderer->groupIndexForVertexBatch == -1);
|
||||
renderer->groupIndexForVertexBatch = 0;
|
||||
}
|
||||
|
||||
|
||||
for (i32 i = 0; i < numVertexes; i++)
|
||||
{
|
||||
targetGroup->vertexList[targetGroup->vertexIndex++] = vertexList[i];
|
||||
@ -315,6 +90,37 @@ INTERNAL void addVertexToRenderGroup_(Renderer *renderer, Texture *tex,
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL inline void addRenderQuadToRenderGroup(Renderer *renderer,
|
||||
RenderQuad_ quad,
|
||||
Texture *tex, v4 color)
|
||||
{
|
||||
/*
|
||||
NOTE(doyle): Entity rendering is always done in two pairs of
|
||||
triangles, i.e. quad. To batch render quads as a triangle strip, we
|
||||
need to create zero-area triangles which OGL will omit from
|
||||
rendering. Render groups are initialised with 1 degenerate vertex and
|
||||
then the first two vertexes sent to the render group are the same to
|
||||
form 1 zero-area triangle strip.
|
||||
|
||||
A degenerate vertex has to be copied from the last vertex in the
|
||||
rendering quad, to repeat this process as more entities are
|
||||
renderered.
|
||||
|
||||
Alternative implementation is recognising if the rendered
|
||||
entity is the first in its render group, then we don't need to init
|
||||
a degenerate vertex, and only at the end of its vertex list. But on
|
||||
subsequent renders, we need a degenerate vertex at the front to
|
||||
create the zero-area triangle strip.
|
||||
|
||||
The first has been chosen for simplicity of code, at the cost of
|
||||
1 degenerate vertex at the start of each render group.
|
||||
*/
|
||||
Vertex vertexList[6] = {quad.vertex[0], quad.vertex[0], quad.vertex[1],
|
||||
quad.vertex[2], quad.vertex[3], quad.vertex[3]};
|
||||
addVertexToRenderGroup(renderer, tex, color, vertexList,
|
||||
ARRAY_COUNT(vertexList));
|
||||
};
|
||||
|
||||
INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY)
|
||||
{
|
||||
if (flipX)
|
||||
@ -332,26 +138,26 @@ INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY)
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL v4 getTexRectNormaliseDeviceCoords(RenderTex renderTex)
|
||||
INTERNAL void updateBufferObject(Renderer *const renderer,
|
||||
const Vertex *const vertexList,
|
||||
const i32 numVertex)
|
||||
{
|
||||
/* Convert texture coordinates to normalised texture coordinates */
|
||||
v4 result = renderTex.texRect;
|
||||
if (renderTex.tex)
|
||||
{
|
||||
v2 texNdcFactor =
|
||||
V2(1.0f / renderTex.tex->width, 1.0f / renderTex.tex->height);
|
||||
result.e[0] *= texNdcFactor.w;
|
||||
result.e[1] *= texNdcFactor.h;
|
||||
result.e[2] *= texNdcFactor.w;
|
||||
result.e[3] *= texNdcFactor.h;
|
||||
}
|
||||
|
||||
return result;
|
||||
// TODO(doyle): We assume that vbo and vao are assigned
|
||||
renderer->numVertexesInVbo = numVertex;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, numVertex * sizeof(Vertex), vertexList,
|
||||
GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
INTERNAL RenderQuad createRenderQuad(Renderer *renderer, v2 pos, v2 size,
|
||||
v2 pivotPoint, Radians rotate,
|
||||
INTERNAL void bufferRenderGroupToGL(Renderer *renderer, RenderGroup *group)
|
||||
{
|
||||
updateBufferObject(renderer, group->vertexList, group->vertexIndex);
|
||||
}
|
||||
|
||||
INTERNAL RenderQuad_ createRenderQuad(Renderer *renderer, v2 pos, v2 size,
|
||||
v2 pivotPoint, f32 rotate,
|
||||
RenderTex renderTex)
|
||||
{
|
||||
/*
|
||||
@ -370,191 +176,134 @@ INTERNAL RenderQuad createRenderQuad(Renderer *renderer, v2 pos, v2 size,
|
||||
v4 vertexPair = {0};
|
||||
vertexPair.vec2[0] = pos;
|
||||
vertexPair.vec2[1] = v2_add(pos, size);
|
||||
v4 texRectNdc = getTexRectNormaliseDeviceCoords(renderTex);
|
||||
|
||||
/* Convert texture coordinates to normalised texture coordinates */
|
||||
v4 texRectNdc = renderTex.texRect;
|
||||
if (renderTex.tex)
|
||||
{
|
||||
v2 texNdcFactor =
|
||||
V2(1.0f / renderTex.tex->width, 1.0f / renderTex.tex->height);
|
||||
texRectNdc.e[0] *= texNdcFactor.w;
|
||||
texRectNdc.e[1] *= texNdcFactor.h;
|
||||
texRectNdc.e[2] *= texNdcFactor.w;
|
||||
texRectNdc.e[3] *= texNdcFactor.h;
|
||||
}
|
||||
|
||||
// NOTE(doyle): Create a quad composed of 4 vertexes to be rendered as
|
||||
// a triangle strip using vertices v0, v1, v2, then v2, v1, v3 (note the
|
||||
// order)
|
||||
v2 vertexList[4] = {0};
|
||||
v2 texCoordList[4] = {0};
|
||||
RenderQuad_ result = {0};
|
||||
result.vertex[0].pos = V2(vertexPair.x, vertexPair.w); // Top left
|
||||
result.vertex[0].texCoord = V2(texRectNdc.x, texRectNdc.w);
|
||||
|
||||
// Top left
|
||||
vertexList[0] = V2(vertexPair.x, vertexPair.w);
|
||||
texCoordList[0] = V2(texRectNdc.x, texRectNdc.w);
|
||||
result.vertex[1].pos = V2(vertexPair.x, vertexPair.y); // Bottom left
|
||||
result.vertex[1].texCoord = V2(texRectNdc.x, texRectNdc.y);
|
||||
|
||||
// Bottom left
|
||||
vertexList[1] = V2(vertexPair.x, vertexPair.y);
|
||||
texCoordList[1] = V2(texRectNdc.x, texRectNdc.y);
|
||||
result.vertex[2].pos = V2(vertexPair.z, vertexPair.w); // Top right
|
||||
result.vertex[2].texCoord = V2(texRectNdc.z, texRectNdc.w);
|
||||
|
||||
// Top right
|
||||
vertexList[2] = V2(vertexPair.z, vertexPair.w);
|
||||
texCoordList[2] = V2(texRectNdc.z, texRectNdc.w);
|
||||
|
||||
// Bottom right
|
||||
vertexList[3] = V2(vertexPair.z, vertexPair.y);
|
||||
texCoordList[3] = V2(texRectNdc.z, texRectNdc.y);
|
||||
result.vertex[3].pos = V2(vertexPair.z, vertexPair.y); // Bottom right
|
||||
result.vertex[3].texCoord = V2(texRectNdc.z, texRectNdc.y);
|
||||
if (rotate == 0) return result;
|
||||
|
||||
// NOTE(doyle): Precalculate rotation on vertex positions
|
||||
// NOTE(doyle): No translation/scale matrix as we pre-calculate it from
|
||||
// entity data and work in world space until GLSL uses the projection matrix
|
||||
|
||||
math_applyRotationToVertexes(pos, pivotPoint, rotate, vertexList,
|
||||
ARRAY_COUNT(vertexList));
|
||||
// NOTE(doyle): Move the world origin to the base position of the object.
|
||||
// Then move the origin to the pivot point (e.g. center of object) and
|
||||
// rotate from that point.
|
||||
v2 pointOfRotation = v2_add(pivotPoint, pos);
|
||||
|
||||
RenderQuad result = {0};
|
||||
|
||||
ASSERT(ARRAY_COUNT(vertexList) == ARRAY_COUNT(result.vertexList));
|
||||
for (i32 i = 0; i < ARRAY_COUNT(vertexList); i++)
|
||||
mat4 rotateMat = mat4_translate(pointOfRotation.x, pointOfRotation.y, 0.0f);
|
||||
rotateMat = mat4_mul(rotateMat, mat4_rotate(rotate, 0.0f, 0.0f, 1.0f));
|
||||
rotateMat = mat4_mul(rotateMat, mat4_translate(-pointOfRotation.x,
|
||||
-pointOfRotation.y, 0.0f));
|
||||
for (i32 i = 0; i < ARRAY_COUNT(result.vertex); i++)
|
||||
{
|
||||
result.vertexList[i].pos = vertexList[i];
|
||||
result.vertexList[i].texCoord = texCoordList[i];
|
||||
// NOTE(doyle): Manual matrix multiplication since vertex pos is 2D and
|
||||
// matrix is 4D
|
||||
v2 oldP = result.vertex[i].pos;
|
||||
v2 newP = {0};
|
||||
|
||||
newP.x = (oldP.x * rotateMat.e[0][0]) + (oldP.y * rotateMat.e[1][0]) +
|
||||
(rotateMat.e[3][0]);
|
||||
newP.y = (oldP.x * rotateMat.e[0][1]) + (oldP.y * rotateMat.e[1][1]) +
|
||||
(rotateMat.e[3][1]);
|
||||
|
||||
result.vertex[i].pos = newP;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline RenderQuad
|
||||
createDefaultTexQuad(Renderer *renderer, RenderTex *renderTex)
|
||||
INTERNAL inline RenderQuad_
|
||||
createDefaultTexQuad(Renderer *renderer, RenderTex renderTex)
|
||||
{
|
||||
RenderQuad result = {0};
|
||||
result = createRenderQuad(renderer, V2(0, 0), V2(0, 0), V2(0, 0), 0.0f,
|
||||
*renderTex);
|
||||
RenderQuad_ result = {0};
|
||||
result = createRenderQuad(renderer, V2(0, 0), V2(0, 0), V2(0, 0),
|
||||
0.0f, renderTex);
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *group)
|
||||
INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup)
|
||||
{
|
||||
/* Load transformation matrix */
|
||||
shader_use(renderer->shader);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
/* Set color modulation value */
|
||||
shader_uniformSetVec4f(renderer->shader, "spriteColor",
|
||||
renderGroup->color);
|
||||
|
||||
/* Send draw calls */
|
||||
#if RENDER_BOUNDING_BOX
|
||||
glBindVertexArray(renderer->vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
|
||||
glBindVertexArray(0);
|
||||
#endif
|
||||
|
||||
Texture *tex = renderGroup->tex;
|
||||
if (tex)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex->id);
|
||||
shader_uniformSet1i(renderer->shader, "tex", 0);
|
||||
}
|
||||
|
||||
glBindVertexArray(renderer->vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
debug_countIncrement(debugcount_drawArrays);
|
||||
#endif
|
||||
|
||||
/* Unbind */
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
GL_CHECK_ERROR();
|
||||
}
|
||||
|
||||
RenderTex renderer_createNullRenderTex(AssetManager *const assetManager)
|
||||
{
|
||||
Texture *emptyTex = asset_texGet(assetManager, "nullTex");
|
||||
Texture *emptyTex = asset_getTex(assetManager, "nullTex");
|
||||
RenderTex result = {emptyTex, V4(0, 1, 1, 0)};
|
||||
return result;
|
||||
}
|
||||
|
||||
void renderer_rect(Renderer *const renderer, Rect camera, v2 pos, v2 size,
|
||||
v2 pivotPoint, Radians rotate, RenderTex *renderTex,
|
||||
v4 color, i32 zDepth, RenderFlags flags)
|
||||
v2 pivotPoint, f32 rotate, RenderTex renderTex, v4 color)
|
||||
{
|
||||
// NOTE(doyle): Bottom left and top right position of quad in world space
|
||||
v2 posInCameraSpace = v2_sub(pos, camera.min);
|
||||
|
||||
RenderTex emptyRenderTex = {0};
|
||||
if (!renderTex)
|
||||
{
|
||||
renderTex = &emptyRenderTex;
|
||||
ASSERT(common_isSet(flags, renderflag_no_texture));
|
||||
}
|
||||
|
||||
RenderQuad quad = createRenderQuad(renderer, posInCameraSpace, size,
|
||||
pivotPoint, rotate, *renderTex);
|
||||
|
||||
beginVertexBatch(renderer);
|
||||
addVertexToRenderGroup_(renderer, renderTex->tex, color, zDepth,
|
||||
quad.vertexList, ARRAY_COUNT(quad.vertexList),
|
||||
rendermode_quad, flags);
|
||||
endVertexBatch(renderer);
|
||||
}
|
||||
|
||||
void renderer_polygon(Renderer *const renderer, Rect camera,
|
||||
v2 *polygonPoints, i32 numPoints, v2 pivotPoint,
|
||||
Radians rotate, RenderTex *renderTex, v4 color, i32 zDepth,
|
||||
RenderFlags flags)
|
||||
{
|
||||
ASSERT(numPoints >= 3);
|
||||
|
||||
{ // Validate polygon is CCW
|
||||
/*
|
||||
NOTE(doyle): Polygon vertexes must be specified in a CCW order!
|
||||
This check utilises the equation for calculating the bounding area
|
||||
of a polygon by decomposing the shapes into line segments and
|
||||
calculating the area under the segment. On cartesian plane, if the
|
||||
polygon is CCW, then by creating these "area" calculatings in
|
||||
sequential order, we'll produce negative valued areas since we
|
||||
determine line segment length by subtracting x2-x1.
|
||||
|
||||
Better explanation over here
|
||||
http://blog.element84.com/polygon-winding.html
|
||||
*/
|
||||
|
||||
f32 areaSum = 0.0f;
|
||||
for (i32 i = 0; i < numPoints - 1; i++)
|
||||
{
|
||||
f32 lengthX = polygonPoints[i + 1].x - polygonPoints[i].x;
|
||||
|
||||
// NOTE(doyle): The height of a line segment is actually (y1 + y2)/2
|
||||
// But since the (1/2) is a constant factor we can get rid of for
|
||||
// checking the winding order..
|
||||
// i.e. a negative number halved is still always negative.
|
||||
f32 lengthY = polygonPoints[i + 1].y + polygonPoints[i].y;
|
||||
|
||||
areaSum += (lengthX * lengthY);
|
||||
}
|
||||
|
||||
f32 lengthX = polygonPoints[0].x - polygonPoints[numPoints - 1].x;
|
||||
f32 lengthY = polygonPoints[0].y + polygonPoints[numPoints - 1].y;
|
||||
areaSum += (lengthX * lengthY);
|
||||
|
||||
if (areaSum < 0)
|
||||
{
|
||||
// NOTE(doyle): Is counter clockwise
|
||||
}
|
||||
else if (areaSum > 0)
|
||||
{
|
||||
// NOTE(doyle): Is clockwise
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE(doyle): CW + CCW combination, i.e. figure 8 shape
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < numPoints; i++)
|
||||
polygonPoints[i] = v2_sub(polygonPoints[i], camera.min);
|
||||
|
||||
// TODO(doyle): Do something with render texture
|
||||
RenderTex emptyRenderTex = {0};
|
||||
if (!renderTex)
|
||||
{
|
||||
renderTex = &emptyRenderTex;
|
||||
ASSERT(common_isSet(flags, renderflag_no_texture));
|
||||
}
|
||||
|
||||
v2 triangulationBaseP = polygonPoints[0];
|
||||
RenderVertex triangulationBaseVertex = {0};
|
||||
triangulationBaseVertex.pos = triangulationBaseP;
|
||||
|
||||
i32 numTrisInTriangulation = numPoints - 2;
|
||||
i32 triangulationIndex = 0;
|
||||
for (i32 i = 1; triangulationIndex < numTrisInTriangulation; i++)
|
||||
{
|
||||
ASSERT((i + 1) < numPoints);
|
||||
|
||||
v2 vertexList[3] = {triangulationBaseP, polygonPoints[i],
|
||||
polygonPoints[i + 1]};
|
||||
|
||||
beginVertexBatch(renderer);
|
||||
|
||||
RenderVertex triangle[3] = {0};
|
||||
triangle[0].pos = vertexList[0];
|
||||
triangle[1].pos = vertexList[1];
|
||||
triangle[2].pos = vertexList[2];
|
||||
|
||||
addVertexToRenderGroup_(renderer, renderTex->tex, color, zDepth,
|
||||
triangle, ARRAY_COUNT(triangle),
|
||||
rendermode_polygon, flags);
|
||||
endVertexBatch(renderer);
|
||||
triangulationIndex++;
|
||||
}
|
||||
v2 posInCameraSpace = v2_sub(pos, camera.pos);
|
||||
RenderQuad_ quad = createRenderQuad(renderer, posInCameraSpace, size,
|
||||
pivotPoint, rotate, renderTex);
|
||||
addRenderQuadToRenderGroup(renderer, quad, renderTex.tex, color);
|
||||
}
|
||||
|
||||
void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera,
|
||||
Font *const font, const char *const string, v2 pos,
|
||||
v2 pivotPoint, Radians rotate, v4 color, i32 zDepth,
|
||||
RenderFlags flags)
|
||||
v2 pivotPoint, f32 rotate, v4 color)
|
||||
{
|
||||
i32 strLen = common_strlen(string);
|
||||
if (strLen <= 0) return;
|
||||
@ -566,16 +315,16 @@ void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera,
|
||||
v2_add(pos, V2((CAST(f32) font->maxSize.w * CAST(f32) strLen),
|
||||
CAST(f32) font->maxSize.h));
|
||||
v2 leftAlignedP = pos;
|
||||
if (math_rectContainsP(camera, leftAlignedP) ||
|
||||
math_rectContainsP(camera, rightAlignedP))
|
||||
if (math_pointInRect(camera, leftAlignedP) ||
|
||||
math_pointInRect(camera, rightAlignedP))
|
||||
{
|
||||
i32 vertexIndex = 0;
|
||||
i32 numVertexPerQuad = 4;
|
||||
i32 numVertexesToAlloc = (strLen * (numVertexPerQuad + 2));
|
||||
RenderVertex *vertexList =
|
||||
memory_pushBytes(arena, numVertexesToAlloc * sizeof(RenderVertex));
|
||||
Vertex *vertexList =
|
||||
memory_pushBytes(arena, numVertexesToAlloc * sizeof(Vertex));
|
||||
|
||||
v2 posInCameraSpace = v2_sub(pos, camera.min);
|
||||
v2 posInCameraSpace = v2_sub(pos, camera.pos);
|
||||
pos = posInCameraSpace;
|
||||
|
||||
// TODO(doyle): Find why font is 1px off, might be arial font semantics
|
||||
@ -590,195 +339,87 @@ void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera,
|
||||
|
||||
/* Get texture out */
|
||||
SubTexture subTexture =
|
||||
asset_atlasGetSubTex(font->atlas, &CAST(char)codepoint);
|
||||
asset_getAtlasSubTex(font->atlas, &CAST(char)codepoint);
|
||||
|
||||
v4 charTexRect = {0};
|
||||
charTexRect.vec2[0] = subTexture.rect.min;
|
||||
charTexRect.vec2[0] = subTexture.rect.pos;
|
||||
charTexRect.vec2[1] =
|
||||
v2_add(subTexture.rect.min, subTexture.rect.max);
|
||||
v2_add(subTexture.rect.pos, subTexture.rect.size);
|
||||
flipTexCoord(&charTexRect, FALSE, TRUE);
|
||||
|
||||
RenderTex renderTex = {tex, charTexRect};
|
||||
RenderQuad quad = createRenderQuad(renderer, pos, font->maxSize,
|
||||
RenderQuad_ quad = createRenderQuad(renderer, pos, font->maxSize,
|
||||
pivotPoint, rotate, renderTex);
|
||||
|
||||
beginVertexBatch(renderer);
|
||||
addVertexToRenderGroup_(renderer, tex, color, zDepth, quad.vertexList,
|
||||
ARRAY_COUNT(quad.vertexList),
|
||||
rendermode_quad, flags);
|
||||
endVertexBatch(renderer);
|
||||
vertexList[vertexIndex++] = quad.vertex[0];
|
||||
for (i32 i = 0; i < ARRAY_COUNT(quad.vertex); i++)
|
||||
{
|
||||
vertexList[vertexIndex++] = quad.vertex[i];
|
||||
}
|
||||
vertexList[vertexIndex++] = quad.vertex[3];
|
||||
pos.x += metric.advance;
|
||||
}
|
||||
|
||||
addVertexToRenderGroup(renderer, tex, color, vertexList,
|
||||
numVertexesToAlloc);
|
||||
// TODO(doyle): Mem free
|
||||
// PLATFORM_MEM_FREE(arena, vertexList,
|
||||
// sizeof(Vertex) * numVertexesToAlloc);
|
||||
}
|
||||
}
|
||||
|
||||
void renderer_entity(Renderer *renderer, MemoryArena_ *transientArena,
|
||||
Rect camera, Entity *entity, v2 pivotPoint, Degrees rotate,
|
||||
v4 color, i32 zDepth, RenderFlags flags)
|
||||
void renderer_entity(Renderer *renderer, Rect camera, Entity *entity,
|
||||
v2 pivotPoint, f32 rotate, v4 color)
|
||||
{
|
||||
// TODO(doyle): Add early exit on entities out of camera bounds
|
||||
Radians totalRotation = DEGREES_TO_RADIANS((entity->rotation + rotate));
|
||||
RenderTex renderTex = {0};
|
||||
if (entity->tex)
|
||||
{
|
||||
EntityAnim *entityAnim = &entity->animList[entity->animListIndex];
|
||||
v4 texRect = {0};
|
||||
if (entityAnim->anim)
|
||||
// TODO(doyle): Batch into render groups
|
||||
|
||||
// NOTE(doyle): Pos + Size since the origin of an entity is it's bottom left
|
||||
// corner. Add the two together so that the clipping point is the far right
|
||||
// side of the entity
|
||||
v2 rightAlignedP = v2_add(entity->pos, entity->hitbox);
|
||||
v2 leftAlignedP = entity->pos;
|
||||
if (math_pointInRect(camera, leftAlignedP) ||
|
||||
math_pointInRect(camera, rightAlignedP))
|
||||
{
|
||||
EntityAnim *entityAnim = &entity->animList[entity->currAnimId];
|
||||
Animation *anim = entityAnim->anim;
|
||||
char *frameName = anim->frameList[entityAnim->currFrame];
|
||||
SubTexture subTex = asset_atlasGetSubTex(anim->atlas, frameName);
|
||||
SubTexture animRect = asset_getAtlasSubTex(anim->atlas, frameName);
|
||||
|
||||
texRect.vec2[0] = subTex.rect.min;
|
||||
texRect.vec2[1] = v2_add(subTex.rect.min, subTex.rect.max);
|
||||
flipTexCoord(&texRect, entity->flipX, entity->flipY);
|
||||
}
|
||||
else
|
||||
{
|
||||
texRect = V4(0.0f, 0.0f, (f32)entity->tex->width,
|
||||
(f32)entity->tex->height);
|
||||
}
|
||||
v4 animTexRect = {0};
|
||||
animTexRect.vec2[0] = animRect.rect.pos;
|
||||
animTexRect.vec2[1] = v2_add(animRect.rect.pos, animRect.rect.size);
|
||||
|
||||
flipTexCoord(&animTexRect, entity->flipX, entity->flipY);
|
||||
|
||||
if (entity->direction == direction_east)
|
||||
{
|
||||
flipTexCoord(&texRect, TRUE, FALSE);
|
||||
flipTexCoord(&animTexRect, TRUE, FALSE);
|
||||
}
|
||||
|
||||
renderTex.tex = entity->tex;
|
||||
renderTex.texRect = texRect;
|
||||
}
|
||||
|
||||
// TODO(doyle): Proper blending
|
||||
v4 renderColor = color;
|
||||
if (v4_equals(color, V4(0, 0, 0, 0))) renderColor = entity->color;
|
||||
|
||||
if (entity->renderMode == rendermode_quad)
|
||||
{
|
||||
renderer_rect(renderer, camera, entity->pos, entity->size,
|
||||
v2_add(entity->offset, pivotPoint), totalRotation,
|
||||
&renderTex, entity->color, zDepth, flags);
|
||||
}
|
||||
else if (entity->renderMode == rendermode_polygon)
|
||||
{
|
||||
ASSERT(entity->numVertexPoints >= 3);
|
||||
ASSERT(entity->vertexPoints);
|
||||
|
||||
v2 *offsetVertexPoints =
|
||||
entity_generateUpdatedVertexList(transientArena, entity);
|
||||
|
||||
renderer_polygon(renderer, camera, offsetVertexPoints,
|
||||
entity->numVertexPoints,
|
||||
v2_add(entity->offset, pivotPoint), totalRotation,
|
||||
&renderTex, renderColor, zDepth, flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
RenderTex renderTex = {entity->tex, animTexRect};
|
||||
renderer_rect(renderer, camera, entity->pos, entity->size, pivotPoint,
|
||||
entity->rotation + rotate, renderTex, color);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(doyle): We have no notion of sort order!!
|
||||
void renderer_renderGroups(Renderer *renderer)
|
||||
{
|
||||
|
||||
/* Sort the group by zdepth */
|
||||
b32 groupHasSwapped = TRUE;
|
||||
i32 numGroupsToCheck = renderer->groupsInUse - 1;
|
||||
while (groupHasSwapped)
|
||||
for (i32 i = 0; i < ARRAY_COUNT(renderer->groups); i++)
|
||||
{
|
||||
groupHasSwapped = FALSE;
|
||||
for (i32 i = 0; i < numGroupsToCheck; i++)
|
||||
RenderGroup *currGroup = &renderer->groups[i];
|
||||
if (currGroup->tex)
|
||||
{
|
||||
RenderGroup *group = &renderer->groups[i];
|
||||
RenderGroup *checkGroup = &renderer->groups[i + 1];
|
||||
|
||||
if (checkGroup->zDepth < group->zDepth)
|
||||
{
|
||||
RenderGroup tmp = *group;
|
||||
*group = *checkGroup;
|
||||
*checkGroup = tmp;
|
||||
|
||||
groupHasSwapped = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
numGroupsToCheck--;
|
||||
}
|
||||
|
||||
/* Render groups */
|
||||
for (i32 i = 0; i < renderer->groupsInUse; i++)
|
||||
{
|
||||
RenderGroup *group = &renderer->groups[i];
|
||||
{ // Buffer render group to OpenGL
|
||||
RenderVertex *vertexList = group->vertexList;
|
||||
i32 numVertex = group->vertexIndex;
|
||||
|
||||
// TODO(doyle): We assume that vbo and vao are assigned
|
||||
renderer->numVertexesInVbo = numVertex;
|
||||
glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[group->mode]);
|
||||
glBufferData(GL_ARRAY_BUFFER, numVertex * sizeof(RenderVertex),
|
||||
vertexList, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
{ // Render buffered data in OpenGl
|
||||
|
||||
ASSERT(group->mode < rendermode_invalid);
|
||||
|
||||
if (group->flags & renderflag_wireframe)
|
||||
{
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
}
|
||||
else
|
||||
{
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
}
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
if (group->flags & renderflag_no_texture)
|
||||
{
|
||||
renderer->activeShaderId =
|
||||
renderer->shaderList[shaderlist_default_no_tex];
|
||||
shaderUse(renderer->activeShaderId);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer->activeShaderId =
|
||||
renderer->shaderList[shaderlist_default];
|
||||
shaderUse(renderer->activeShaderId);
|
||||
Texture *tex = group->tex;
|
||||
if (tex)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, tex->id);
|
||||
shaderUniformSet1i(renderer->activeShaderId, "tex", 0);
|
||||
GL_CHECK_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
glDisable(GL_CULL_FACE);
|
||||
#endif
|
||||
|
||||
/* Set color modulation value */
|
||||
shaderUniformSetVec4f(renderer->activeShaderId, "spriteColor",
|
||||
group->color);
|
||||
|
||||
glBindVertexArray(renderer->vao[group->mode]);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
|
||||
GL_CHECK_ERROR();
|
||||
debug_countIncrement(debugcount_drawArrays);
|
||||
|
||||
/* Unbind */
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
GL_CHECK_ERROR();
|
||||
}
|
||||
bufferRenderGroupToGL(renderer, currGroup);
|
||||
renderGLBufferedData(renderer, currGroup);
|
||||
|
||||
RenderGroup cleanGroup = {0};
|
||||
cleanGroup.vertexList = group->vertexList;
|
||||
*group = cleanGroup;
|
||||
cleanGroup.vertexList = currGroup->vertexList;
|
||||
*currGroup = cleanGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
renderer->groupsInUse = 0;
|
||||
}
|
||||
|
25
src/Shader.c
Normal file
25
src/Shader.c
Normal file
@ -0,0 +1,25 @@
|
||||
#include "Dengine/Shader.h"
|
||||
|
||||
void shader_uniformSet1i(Shader *const shader, const GLchar *name,
|
||||
const GLuint data)
|
||||
{
|
||||
GLint uniformLoc = glGetUniformLocation(shader->id, name);
|
||||
glUniform1i(uniformLoc, data);
|
||||
}
|
||||
|
||||
void shader_uniformSetMat4fv(Shader *const shader, const GLchar *name,
|
||||
mat4 data)
|
||||
{
|
||||
GLint uniformLoc = glGetUniformLocation(shader->id, name);
|
||||
glUniformMatrix4fv(uniformLoc, 1, GL_FALSE, data.e[0]);
|
||||
}
|
||||
|
||||
void shader_uniformSetVec4f(Shader *const shader, const GLchar *name,
|
||||
v4 data)
|
||||
{
|
||||
GLint uniformLoc = glGetUniformLocation(shader->id, name);
|
||||
glUniform4f(uniformLoc, data.e[0], data.e[1], data.e[2], data.e[3]);
|
||||
}
|
||||
|
||||
|
||||
void shader_use(const Shader *const shader) { glUseProgram(shader->id); }
|
120
src/String.c
Normal file
120
src/String.c
Normal file
@ -0,0 +1,120 @@
|
||||
#include "Dengine/String.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
|
||||
/*
|
||||
* +-------------------------------------+
|
||||
* | Header | C-String | Null Terminator |
|
||||
* +-------------------------------------+
|
||||
* |
|
||||
* +--> Functions return the c-string for compatibility with other
|
||||
* string libraries
|
||||
*
|
||||
* Headers are retrieved using pointer arithmetric from the C string. These
|
||||
* strings are typechecked by their own typedef char String.
|
||||
*/
|
||||
|
||||
typedef struct StringHeader
|
||||
{
|
||||
i32 len;
|
||||
|
||||
// NOTE(doyle): A string is stored as one contiguous chunk of memory. We
|
||||
// don't use a pointer for storing the string as this'd require an extra
|
||||
// 4 bytes to store the pointer, which we don't need if everything is
|
||||
// contiguous. The string follows on from the len, and we return the address
|
||||
// of the string to simulate a pointer.
|
||||
String string;
|
||||
} StringHeader;
|
||||
|
||||
// TODO(doyle): string capacity- append if already enough space
|
||||
INTERNAL StringHeader *string_getHeader(String *const string)
|
||||
{
|
||||
StringHeader *result = NULL;
|
||||
|
||||
// NOTE(doyle): C-String must be located at end of struct type for offset to
|
||||
// be correct! We cannot just subtract the string-header since we start at
|
||||
// the string ptr position
|
||||
if (string)
|
||||
{
|
||||
i32 byteOffsetToHeader = sizeof(StringHeader) - sizeof(String *);
|
||||
result = CAST(StringHeader *)((CAST(u8 *) string) - byteOffsetToHeader);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
i32 string_len(String *const string)
|
||||
{
|
||||
if (!string) return -1;
|
||||
|
||||
StringHeader *header = string_getHeader(string);
|
||||
i32 result = header->len;
|
||||
return result;
|
||||
}
|
||||
|
||||
String *const string_append(MemoryArena_ *const arena, String *oldString,
|
||||
char *appendString, i32 appendLen)
|
||||
|
||||
{
|
||||
if (!oldString || !appendString || !arena) return oldString;
|
||||
|
||||
/* Calculate size of new string */
|
||||
StringHeader *oldHeader = string_getHeader(oldString);
|
||||
i32 newLen = oldHeader->len + appendLen;
|
||||
String *newString = string_makeLen(arena, newLen);
|
||||
|
||||
/* Append strings together */
|
||||
String *insertPtr = newString;
|
||||
common_strncpy(insertPtr, oldString, oldHeader->len);
|
||||
insertPtr += oldHeader->len;
|
||||
common_strncpy(insertPtr, appendString, appendLen);
|
||||
|
||||
/* Free old string */
|
||||
string_free(arena, oldString);
|
||||
|
||||
return newString;
|
||||
}
|
||||
|
||||
void string_free(MemoryArena_ *arena, String *string)
|
||||
{
|
||||
if (!string || !arena) return;
|
||||
|
||||
StringHeader *header = string_getHeader(string);
|
||||
i32 bytesToFree = sizeof(StringHeader) + header->len;
|
||||
|
||||
common_memset((u8 *)header, 0, bytesToFree);
|
||||
|
||||
// TODO(doyle): Mem free
|
||||
// PLATFORM_MEM_FREE(arena, header, bytesToFree);
|
||||
|
||||
string = NULL;
|
||||
}
|
||||
|
||||
String *const string_make(MemoryArena_ *const arena, char *string)
|
||||
{
|
||||
if (!arena) return NULL;
|
||||
|
||||
i32 len = common_strlen(string);
|
||||
String *result = string_makeLen(arena, len);
|
||||
common_strncpy(result, string, len);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String *const string_makeLen(MemoryArena_ *const arena, i32 len)
|
||||
{
|
||||
if (!arena) return NULL;
|
||||
|
||||
// NOTE(doyle): Allocate the string header size plus the len. But _note_
|
||||
// that StringHeader contains a single String character. This has
|
||||
// a side-effect of already preallocating a byte for the null-terminating
|
||||
// character. Whilst the len of a string counts up to the last character
|
||||
// _not_ including null-terminator.
|
||||
i32 bytesToAllocate = sizeof(StringHeader) + len;
|
||||
void *chunk = memory_pushBytes(arena, bytesToAllocate * sizeof(u8));
|
||||
if (!chunk) return NULL;
|
||||
|
||||
StringHeader *header = CAST(StringHeader *) chunk;
|
||||
header->len = len;
|
||||
return &header->string;
|
||||
}
|
||||
|
78
src/Texture.c
Normal file
78
src/Texture.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include "Dengine/Texture.h"
|
||||
|
||||
enum BytesPerPixel
|
||||
{
|
||||
bytesPerPixel_Greyscale = 1,
|
||||
bytesPerPixel_GreyscaleAlpha = 2,
|
||||
bytesPerPixel_RGB = 3,
|
||||
bytesPerPixel_RGBA = 4,
|
||||
};
|
||||
|
||||
INTERNAL GLint getGLFormat(i32 bytesPerPixel, b32 srgb)
|
||||
{
|
||||
switch (bytesPerPixel)
|
||||
{
|
||||
case bytesPerPixel_Greyscale:
|
||||
return GL_LUMINANCE;
|
||||
case bytesPerPixel_GreyscaleAlpha:
|
||||
return GL_LUMINANCE_ALPHA;
|
||||
case bytesPerPixel_RGB:
|
||||
return (srgb ? GL_SRGB : GL_RGB);
|
||||
case bytesPerPixel_RGBA:
|
||||
return (srgb ? GL_SRGB_ALPHA : GL_RGBA);
|
||||
default:
|
||||
// TODO(doyle): Invalid
|
||||
// std::cout << "getGLFormat() invalid bytesPerPixel: "
|
||||
// << bytesPerPixel << std::endl;
|
||||
return GL_LUMINANCE;
|
||||
}
|
||||
}
|
||||
|
||||
Texture texture_gen(const GLuint width, const GLuint height,
|
||||
const GLint bytesPerPixel, const u8 *const image)
|
||||
{
|
||||
// TODO(doyle): Let us set the parameters gl params as well
|
||||
GL_CHECK_ERROR();
|
||||
Texture tex = {0};
|
||||
tex.width = width;
|
||||
tex.height = height;
|
||||
tex.internalFormat = GL_RGBA;
|
||||
tex.wrapS = GL_REPEAT;
|
||||
tex.wrapT = GL_REPEAT;
|
||||
tex.filterMinification = GL_NEAREST;
|
||||
tex.filterMagnification = GL_NEAREST;
|
||||
|
||||
glGenTextures(1, &tex.id);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex.id);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
/* Load image into texture */
|
||||
// TODO(doyle) Figure out the gl format
|
||||
tex.imageFormat = getGLFormat(bytesPerPixel, FALSE);
|
||||
ASSERT(tex.imageFormat == GL_RGBA);
|
||||
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, tex.internalFormat, tex.width, tex.height, 0,
|
||||
tex.imageFormat, GL_UNSIGNED_BYTE, image);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
// TODO(doyle): Not needed for sprites? glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
/* Set parameter of currently bound texture */
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex.wrapS);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex.wrapT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
tex.filterMinification);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
|
||||
tex.filterMagnification);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
/* Unbind and clean up */
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
return tex;
|
||||
}
|
356
src/Ui.c
356
src/Ui.c
@ -1,356 +0,0 @@
|
||||
#include "Dengine/Ui.h"
|
||||
#include "Dengine/AssetManager.h"
|
||||
#include "Dengine/Assets.h"
|
||||
#include "Dengine/Asteroid.h"
|
||||
#include "Dengine/Debug.h"
|
||||
#include "Dengine/Renderer.h"
|
||||
|
||||
void ui_beginState(UiState *state) { state->hotItem = 0; }
|
||||
|
||||
void ui_endState(UiState *state, InputBuffer *input)
|
||||
{
|
||||
if (!common_isSet(input->keys[keycode_mouseLeft].flags,
|
||||
keystateflag_ended_down))
|
||||
{
|
||||
state->activeItem = 0;
|
||||
}
|
||||
else if (state->activeItem == 0)
|
||||
{
|
||||
state->activeItem = -1;
|
||||
}
|
||||
|
||||
if (state->keyEntered == keycode_tab) state->kbdItem = 0;
|
||||
|
||||
state->keyEntered = keycode_null;
|
||||
state->keyChar = keycode_null;
|
||||
}
|
||||
|
||||
i32 ui_button(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager, Renderer *const renderer,
|
||||
Font *const font, const InputBuffer input, const i32 id,
|
||||
const Rect rect, const char *const label, i32 zDepth)
|
||||
{
|
||||
if (math_rectContainsP(rect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == 0)
|
||||
{
|
||||
if (common_isSet(input.keys[keycode_mouseLeft].flags,
|
||||
keystateflag_ended_down))
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Draw shadow
|
||||
renderer_staticRect(renderer, v2_add(V2(1, 1), rect.min), rect.size,
|
||||
V2(0, 0), 0, renderTex, V4(0, 0, 0, 1));
|
||||
#endif
|
||||
|
||||
v2 buttonOffset = V2(0, 0);
|
||||
v4 buttonColor = V4(0, 0, 0, 1);
|
||||
if (uiState->hotItem == id)
|
||||
{
|
||||
if (uiState->activeItem == id)
|
||||
{
|
||||
buttonOffset = V2(1, 1);
|
||||
buttonColor = V4(0.8f, 0.8f, 0.8f, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE(doyle): Hover effect
|
||||
buttonColor = V4(0.05f, 0.05f, 0.05f, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* If no widget has keyboard focus, take it */
|
||||
if (uiState->kbdItem == 0) uiState->kbdItem = id;
|
||||
|
||||
v2 rectSize = math_rectGetSize(rect);
|
||||
/* If we have keyboard focus, show it */
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
// Draw outline
|
||||
renderer_rectFixed(renderer,
|
||||
v2_add(V2(-2, -2), v2_add(buttonOffset, rect.min)),
|
||||
v2_add(V2(4, 4), rectSize), V2(0, 0), 0, NULL,
|
||||
buttonColor, zDepth, renderflag_no_texture);
|
||||
}
|
||||
|
||||
renderer_rectFixed(renderer, v2_add(buttonOffset, rect.min), rectSize,
|
||||
V2(0, 0), 0, NULL, buttonColor, zDepth,
|
||||
renderflag_no_texture);
|
||||
|
||||
if (label)
|
||||
{
|
||||
v2 labelDim = asset_fontStringDimInPixels(font, label);
|
||||
v2 labelPos = rect.min;
|
||||
|
||||
// Initially position the label to half the width of the button
|
||||
labelPos.x += (rectSize.w * 0.5f);
|
||||
|
||||
// Move the label pos back half the length of the string (i.e.
|
||||
// center it)
|
||||
labelPos.x -= (CAST(f32) labelDim.w * 0.5f);
|
||||
|
||||
if (labelDim.h < rectSize.h)
|
||||
{
|
||||
labelPos.y += (rectSize.h * 0.5f);
|
||||
labelPos.y -= (CAST(f32) labelDim.h * 0.5f);
|
||||
}
|
||||
|
||||
// TODO(doyle): We're using odd colors to overcome z-sorting by forcing
|
||||
// button text into another render group
|
||||
labelPos = v2_add(labelPos, buttonOffset);
|
||||
renderer_stringFixed(renderer, arena, font, label, labelPos, V2(0, 0),
|
||||
0, V4(0.9f, 0.9f, 0.9f, 0.9f), zDepth, 0);
|
||||
}
|
||||
|
||||
// After renderering before click check, see if we need to process keys
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
switch (uiState->keyEntered)
|
||||
{
|
||||
case keycode_tab:
|
||||
// Set focus to nothing and let next widget get focus
|
||||
uiState->kbdItem = 0;
|
||||
if (uiState->keyMod == keycode_leftShift)
|
||||
uiState->kbdItem = uiState->lastWidget;
|
||||
|
||||
// Clear key state so next widget doesn't auto grab
|
||||
uiState->keyEntered = keycode_null;
|
||||
break;
|
||||
case keycode_enter:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uiState->lastWidget = id;
|
||||
|
||||
// If button is hot and active, but mouse button is not
|
||||
// down, the user must have clicked the button.
|
||||
if (!common_isSet(input.keys[keycode_mouseLeft].flags,
|
||||
keystateflag_ended_down) &&
|
||||
uiState->hotItem == id && uiState->activeItem == id)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
i32 ui_scrollbar(UiState *const uiState, AssetManager *const assetManager,
|
||||
Renderer *const renderer, const InputBuffer input,
|
||||
const i32 id, const Rect scrollBarRect, i32 *const value,
|
||||
const i32 maxValue, i32 zDepth)
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(*value <= maxValue);
|
||||
#endif
|
||||
|
||||
if (math_rectContainsP(scrollBarRect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == 0)
|
||||
{
|
||||
if (common_isSet(input.keys[keycode_mouseLeft].flags,
|
||||
keystateflag_ended_down))
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
|
||||
|
||||
/* If no widget has keyboard focus, take it */
|
||||
if (uiState->kbdItem == 0) uiState->kbdItem = id;
|
||||
|
||||
v2 rectSize = math_rectGetSize(scrollBarRect);
|
||||
/* If we have keyboard focus, show it */
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
// Draw outline
|
||||
renderer_rectFixed(renderer, v2_add(V2(-2, -2), scrollBarRect.min),
|
||||
v2_add(V2(4, 4), rectSize), V2(0, 0), 0, &renderTex,
|
||||
V4(1, 0, 0, 1), zDepth, 0);
|
||||
}
|
||||
|
||||
// Render scroll bar background
|
||||
renderer_rectFixed(renderer, scrollBarRect.min, rectSize, V2(0, 0), 0,
|
||||
&renderTex, V4(0.75f, 0.5f, 0.5f, 1), zDepth, 0);
|
||||
|
||||
// Render scroll bar slider
|
||||
v2 sliderSize = V2(16, 16);
|
||||
v4 sliderColor = V4(0, 0, 0, 1);
|
||||
|
||||
f32 sliderPercentageOffset = (CAST(f32) * value / CAST(f32) maxValue);
|
||||
f32 sliderYOffsetToBar =
|
||||
(rectSize.h - sliderSize.h) * sliderPercentageOffset;
|
||||
v2 sliderPos = v2_add(scrollBarRect.min, V2(0, sliderYOffsetToBar));
|
||||
|
||||
if (uiState->hotItem == id || uiState->activeItem == id)
|
||||
sliderColor = V4(1.0f, 0, 0, 1);
|
||||
else
|
||||
sliderColor = V4(0.0f, 1.0f, 0, 1);
|
||||
|
||||
renderer_rectFixed(renderer, sliderPos, sliderSize, V2(0, 0), 0, &renderTex,
|
||||
sliderColor, zDepth, 0);
|
||||
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
switch (uiState->keyEntered)
|
||||
{
|
||||
case keycode_tab:
|
||||
uiState->kbdItem = 0;
|
||||
if (uiState->keyMod == keycode_leftShift)
|
||||
uiState->kbdItem = uiState->lastWidget;
|
||||
|
||||
// Clear key state so next widget doesn't auto grab
|
||||
uiState->keyEntered = keycode_null;
|
||||
break;
|
||||
case keycode_up:
|
||||
// TODO(doyle): Fix input for this to work, i.e. proper rate limited
|
||||
// input poll
|
||||
if (*value < maxValue)
|
||||
{
|
||||
(*value)++;
|
||||
return id;
|
||||
}
|
||||
case keycode_down:
|
||||
if (*value > 0)
|
||||
{
|
||||
(*value)--;
|
||||
return id;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uiState->lastWidget = id;
|
||||
|
||||
if (uiState->activeItem == id)
|
||||
{
|
||||
f32 mouseYRelToRect = input.mouseP.y - scrollBarRect.min.y;
|
||||
|
||||
// Bounds check
|
||||
if (mouseYRelToRect < 0)
|
||||
mouseYRelToRect = 0;
|
||||
else if (mouseYRelToRect > rectSize.h)
|
||||
mouseYRelToRect = rectSize.h;
|
||||
|
||||
f32 newSliderPercentOffset = (CAST(f32) mouseYRelToRect / rectSize.h);
|
||||
|
||||
i32 newValue = CAST(i32)(newSliderPercentOffset * CAST(f32) maxValue);
|
||||
if (newValue != *value)
|
||||
{
|
||||
*value = newValue;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
i32 ui_textfield(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager, Renderer *const renderer,
|
||||
Font *const font, InputBuffer input, const i32 id,
|
||||
const Rect rect, char *const string, i32 zDepth)
|
||||
{
|
||||
i32 strLen = common_strlen(string);
|
||||
b32 changed = FALSE;
|
||||
|
||||
if (math_rectContainsP(rect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == 0)
|
||||
{
|
||||
if (common_isSet(input.keys[keycode_mouseLeft].flags,
|
||||
keystateflag_ended_down))
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If no widget has keyboard focus, take it */
|
||||
if (uiState->kbdItem == 0) uiState->kbdItem = id;
|
||||
|
||||
v2 rectSize = math_rectGetSize(rect);
|
||||
/* If we have keyboard focus, show it */
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
// Draw outline
|
||||
renderer_rectFixed(renderer, v2_add(V2(-2, -2), rect.min),
|
||||
v2_add(V2(4, 4), rectSize), V2(0, 0), 0, NULL,
|
||||
V4(1.0f, 0, 0, 1), zDepth, 0);
|
||||
}
|
||||
|
||||
// Render text field
|
||||
renderer_rectFixed(renderer, rect.min, rectSize, V2(0, 0), 0, NULL,
|
||||
V4(0.75f, 0.5f, 0.5f, 1), zDepth, 0);
|
||||
|
||||
if (uiState->activeItem == id || uiState->hotItem == id)
|
||||
{
|
||||
renderer_rectFixed(renderer, rect.min, rectSize, V2(0, 0), 0, NULL,
|
||||
V4(0.75f, 0.75f, 0.0f, 1), zDepth, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer_rectFixed(renderer, rect.min, rectSize, V2(0, 0), 0, NULL,
|
||||
V4(0.5f, 0.5f, 0.5f, 1), zDepth, 0);
|
||||
}
|
||||
|
||||
v2 strPos = rect.min;
|
||||
renderer_stringFixed(renderer, arena, font, string, strPos, V2(0, 0), 0,
|
||||
V4(0, 0, 0, 1), zDepth, 0);
|
||||
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
switch (uiState->keyEntered)
|
||||
{
|
||||
case keycode_tab:
|
||||
uiState->kbdItem = 0;
|
||||
if (uiState->keyMod == keycode_leftShift)
|
||||
uiState->kbdItem = uiState->lastWidget;
|
||||
|
||||
uiState->keyEntered = keycode_null;
|
||||
break;
|
||||
|
||||
case keycode_backspace:
|
||||
if (strLen > 0)
|
||||
{
|
||||
string[--strLen] = 0;
|
||||
changed = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uiState->keyChar >= keycode_space &&
|
||||
uiState->keyChar <= keycode_tilda && strLen < 30)
|
||||
{
|
||||
string[strLen++] = uiState->keyChar + ' ';
|
||||
string[strLen] = 0;
|
||||
changed = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!common_isSet(input.keys[keycode_mouseLeft].flags,
|
||||
keystateflag_ended_down) &&
|
||||
uiState->hotItem == id && uiState->activeItem == id)
|
||||
{
|
||||
uiState->kbdItem = id;
|
||||
}
|
||||
|
||||
uiState->lastWidget = id;
|
||||
|
||||
if (changed) return id;
|
||||
return 0;
|
||||
}
|
470
src/UserInterface.c
Normal file
470
src/UserInterface.c
Normal file
@ -0,0 +1,470 @@
|
||||
#include "Dengine/UserInterface.h"
|
||||
#include "Dengine/AssetManager.h"
|
||||
#include "Dengine/Assets.h"
|
||||
#include "Dengine/Debug.h"
|
||||
#include "Dengine/Renderer.h"
|
||||
|
||||
i32 userInterface_button(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer, Font *const font,
|
||||
const KeyInput input, const i32 id, const Rect rect,
|
||||
const char *const label)
|
||||
{
|
||||
if (math_pointInRect(rect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
|
||||
// NOTE(doyle): UI windows are drawn first, they steal focus on mouse
|
||||
// click since window logic is paired with the window rendering. If
|
||||
// a UI element resides over our mouse, we allow the element to override
|
||||
// the windows focus
|
||||
// TODO(doyle): Make a window list to iterate over window ids
|
||||
if (uiState->activeItem == uiState->statWindow.id ||
|
||||
uiState->activeItem == uiState->debugWindow.id ||
|
||||
uiState->activeItem == 0)
|
||||
{
|
||||
if (input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
|
||||
|
||||
#if 0
|
||||
// Draw shadow
|
||||
renderer_staticRect(renderer, v2_add(V2(1, 1), rect.pos), rect.size,
|
||||
V2(0, 0), 0, renderTex, V4(0, 0, 0, 1));
|
||||
#endif
|
||||
|
||||
v2 buttonOffset = V2(0, 0);
|
||||
v4 buttonColor = {0};
|
||||
if (uiState->hotItem == id)
|
||||
{
|
||||
if (uiState->activeItem == id)
|
||||
{
|
||||
buttonOffset = V2(1, 1);
|
||||
buttonColor = V4(1, 1, 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(doyle): Optional add effect on button hover
|
||||
buttonColor = V4(1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonColor = V4(0.5f, 0.5f, 0.5f, 1);
|
||||
}
|
||||
|
||||
/* If no widget has keyboard focus, take it */
|
||||
if (uiState->kbdItem == 0)
|
||||
uiState->kbdItem = id;
|
||||
|
||||
/* If we have keyboard focus, show it */
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
// Draw outline
|
||||
renderer_staticRect(
|
||||
renderer, v2_add(V2(-2, -2), v2_add(buttonOffset, rect.pos)),
|
||||
v2_add(V2(4, 4), rect.size), V2(0, 0), 0, renderTex, buttonColor);
|
||||
}
|
||||
|
||||
renderer_staticRect(renderer, v2_add(buttonOffset, rect.pos), rect.size,
|
||||
V2(0, 0), 0, renderTex, buttonColor);
|
||||
|
||||
if (label)
|
||||
{
|
||||
v2 labelDim = asset_stringDimInPixels(font, label);
|
||||
v2 labelPos = rect.pos;
|
||||
|
||||
// Initially position the label to half the width of the button
|
||||
labelPos.x += (rect.size.w * 0.5f);
|
||||
|
||||
// Move the label pos back half the length of the string (i.e.
|
||||
// center it)
|
||||
labelPos.x -= (CAST(f32) labelDim.w * 0.5f);
|
||||
|
||||
if (labelDim.h < rect.size.h)
|
||||
{
|
||||
labelPos.y += (rect.size.h * 0.5f);
|
||||
labelPos.y -= (CAST(f32)labelDim.h * 0.5f);
|
||||
}
|
||||
|
||||
labelPos = v2_add(labelPos, buttonOffset);
|
||||
renderer_staticString(renderer, arena, font, label, labelPos, V2(0, 0),
|
||||
0, V4(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
// After renderering before click check, see if we need to process keys
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
switch (uiState->keyEntered)
|
||||
{
|
||||
case keycode_tab:
|
||||
// Set focus to nothing and let next widget get focus
|
||||
uiState->kbdItem = 0;
|
||||
if (uiState->keyMod == keycode_leftShift)
|
||||
uiState->kbdItem = uiState->lastWidget;
|
||||
|
||||
// Clear key state so next widget doesn't auto grab
|
||||
uiState->keyEntered = keycode_null;
|
||||
break;
|
||||
case keycode_enter:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uiState->lastWidget = id;
|
||||
|
||||
// If button is hot and active, but mouse button is not
|
||||
// down, the user must have clicked the button.
|
||||
if (!input.keys[keycode_mouseLeft].endedDown &&
|
||||
uiState->hotItem == id &&
|
||||
uiState->activeItem == id)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
i32 userInterface_scrollbar(UiState *const uiState,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer, const KeyInput input,
|
||||
const i32 id, const Rect scrollBarRect,
|
||||
i32 *const value, const i32 maxValue)
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(*value <= maxValue);
|
||||
#endif
|
||||
|
||||
if (math_pointInRect(scrollBarRect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == uiState->statWindow.id ||
|
||||
uiState->activeItem == uiState->debugWindow.id ||
|
||||
uiState->activeItem == 0)
|
||||
{
|
||||
if (input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
|
||||
|
||||
/* If no widget has keyboard focus, take it */
|
||||
if (uiState->kbdItem == 0)
|
||||
uiState->kbdItem = id;
|
||||
|
||||
/* If we have keyboard focus, show it */
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
// Draw outline
|
||||
renderer_staticRect(renderer, v2_add(V2(-2, -2), scrollBarRect.pos),
|
||||
v2_add(V2(4, 4), scrollBarRect.size), V2(0, 0), 0,
|
||||
renderTex, V4(1, 0, 0, 1));
|
||||
}
|
||||
|
||||
// Render scroll bar background
|
||||
renderer_staticRect(renderer, scrollBarRect.pos, scrollBarRect.size,
|
||||
V2(0, 0), 0, renderTex, V4(0.75f, 0.5f, 0.5f, 1));
|
||||
|
||||
// Render scroll bar slider
|
||||
v2 sliderSize = V2(16, 16);
|
||||
v4 sliderColor = V4(0, 0, 0, 1);
|
||||
|
||||
f32 sliderPercentageOffset = (CAST(f32) *value / CAST(f32) maxValue);
|
||||
f32 sliderYOffsetToBar =
|
||||
(scrollBarRect.size.h - sliderSize.h) * sliderPercentageOffset;
|
||||
v2 sliderPos = v2_add(scrollBarRect.pos, V2(0, sliderYOffsetToBar));
|
||||
|
||||
if (uiState->hotItem == id || uiState->activeItem == id)
|
||||
sliderColor = V4(1.0f, 0, 0, 1);
|
||||
else
|
||||
sliderColor = V4(0.0f, 1.0f, 0, 1);
|
||||
|
||||
renderer_staticRect(renderer, sliderPos, sliderSize, V2(0, 0), 0, renderTex,
|
||||
sliderColor);
|
||||
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
switch (uiState->keyEntered)
|
||||
{
|
||||
case keycode_tab:
|
||||
uiState->kbdItem = 0;
|
||||
if (uiState->keyMod == keycode_leftShift)
|
||||
uiState->kbdItem = uiState->lastWidget;
|
||||
|
||||
// Clear key state so next widget doesn't auto grab
|
||||
uiState->keyEntered = keycode_null;
|
||||
break;
|
||||
case keycode_up:
|
||||
// TODO(doyle): Fix input for this to work, i.e. proper rate limited input poll
|
||||
if (*value < maxValue)
|
||||
{
|
||||
(*value)++;
|
||||
return id;
|
||||
}
|
||||
case keycode_down:
|
||||
if (*value > 0)
|
||||
{
|
||||
(*value)--;
|
||||
return id;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uiState->lastWidget = id;
|
||||
|
||||
if (uiState->activeItem == id)
|
||||
{
|
||||
f32 mouseYRelToRect = input.mouseP.y - scrollBarRect.pos.y;
|
||||
|
||||
// Bounds check
|
||||
if (mouseYRelToRect < 0)
|
||||
mouseYRelToRect = 0;
|
||||
else if (mouseYRelToRect > scrollBarRect.size.h)
|
||||
mouseYRelToRect = scrollBarRect.size.h;
|
||||
|
||||
f32 newSliderPercentOffset =
|
||||
(CAST(f32) mouseYRelToRect / scrollBarRect.size.h);
|
||||
|
||||
i32 newValue = CAST(i32)(newSliderPercentOffset * CAST(f32)maxValue);
|
||||
if (newValue != *value)
|
||||
{
|
||||
*value = newValue;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
i32 userInterface_textField(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer, Font *const font,
|
||||
KeyInput input, const i32 id, const Rect rect,
|
||||
char *const string)
|
||||
{
|
||||
i32 strLen = common_strlen(string);
|
||||
b32 changed = FALSE;
|
||||
|
||||
if (math_pointInRect(rect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == uiState->statWindow.id ||
|
||||
uiState->activeItem == uiState->debugWindow.id ||
|
||||
uiState->activeItem == 0)
|
||||
{
|
||||
if (input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If no widget has keyboard focus, take it */
|
||||
if (uiState->kbdItem == 0)
|
||||
uiState->kbdItem = id;
|
||||
|
||||
RenderTex renderTex = renderer_createNullRenderTex(assetManager);
|
||||
/* If we have keyboard focus, show it */
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
// Draw outline
|
||||
renderer_staticRect(renderer, v2_add(V2(-2, -2), rect.pos),
|
||||
v2_add(V2(4, 4), rect.size), V2(0, 0), 0,
|
||||
renderTex, V4(1.0f, 0, 0, 1));
|
||||
}
|
||||
|
||||
// Render text field
|
||||
renderer_staticRect(renderer, rect.pos, rect.size, V2(0, 0), 0,
|
||||
renderTex, V4(0.75f, 0.5f, 0.5f, 1));
|
||||
|
||||
if (uiState->activeItem == id || uiState->hotItem == id)
|
||||
{
|
||||
renderer_staticRect(renderer, rect.pos, rect.size, V2(0, 0), 0,
|
||||
renderTex, V4(0.75f, 0.75f, 0.0f, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer_staticRect(renderer, rect.pos, rect.size, V2(0, 0), 0,
|
||||
renderTex, V4(0.5f, 0.5f, 0.5f, 1));
|
||||
}
|
||||
|
||||
v2 strPos = rect.pos;
|
||||
renderer_staticString(renderer, arena, font, string, strPos, V2(0, 0), 0,
|
||||
V4(0, 0, 0, 1));
|
||||
|
||||
if (uiState->kbdItem == id)
|
||||
{
|
||||
switch (uiState->keyEntered)
|
||||
{
|
||||
case keycode_tab:
|
||||
uiState->kbdItem = 0;
|
||||
if (uiState->keyMod == keycode_leftShift)
|
||||
uiState->kbdItem = uiState->lastWidget;
|
||||
|
||||
uiState->keyEntered = keycode_null;
|
||||
break;
|
||||
|
||||
case keycode_backspace:
|
||||
if (strLen > 0)
|
||||
{
|
||||
string[--strLen] = 0;
|
||||
changed = TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uiState->keyChar >= keycode_space &&
|
||||
uiState->keyChar <= keycode_tilda && strLen < 30)
|
||||
{
|
||||
string[strLen++] = uiState->keyChar + ' ';
|
||||
string[strLen] = 0;
|
||||
changed = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!input.keys[keycode_mouseLeft].endedDown &&
|
||||
uiState->hotItem == id &&
|
||||
uiState->activeItem == id)
|
||||
{
|
||||
uiState->kbdItem = id;
|
||||
}
|
||||
|
||||
uiState->lastWidget = id;
|
||||
|
||||
if (changed) return id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i32 userInterface_window(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer, Font *const font,
|
||||
const KeyInput input, WindowState *window)
|
||||
{
|
||||
if (math_pointInRect(window->rect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = window->id;
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
|
||||
uiState->activeItem = window->id;
|
||||
}
|
||||
|
||||
Rect rect = window->rect;
|
||||
RenderTex nullRenderTex = renderer_createNullRenderTex(assetManager);
|
||||
renderer_staticRect(renderer, rect.pos, rect.size, V2(0, 0), 0,
|
||||
nullRenderTex, V4(0.25f, 0.25f, 0.5f, 0.5f));
|
||||
|
||||
v2 menuTitleP = v2_add(rect.pos, V2(0, rect.size.h - 10));
|
||||
renderer_staticString(renderer, arena, font, window->title, menuTitleP,
|
||||
V2(0, 0), 0, V4(0, 0, 0, 1));
|
||||
|
||||
/* Draw window elements */
|
||||
i32 firstActiveChildId = -1;
|
||||
for (i32 i = 0; i < window->numChildUiItems; i++)
|
||||
{
|
||||
UiItem *childUi = &window->childUiItems[i];
|
||||
|
||||
// TODO(doyle): Redundant? If we can only have 1 active child at a time
|
||||
// What about overlapping elements?
|
||||
i32 getChildActiveState = -1;
|
||||
switch(childUi->type)
|
||||
{
|
||||
case uitype_button:
|
||||
// TODO(doyle): Bug in font rendering once button reaches 700-800+
|
||||
// pixels
|
||||
getChildActiveState = userInterface_button(
|
||||
uiState, arena, assetManager, renderer, font, input,
|
||||
childUi->id, childUi->rect, childUi->label);
|
||||
break;
|
||||
|
||||
case uitype_scrollbar:
|
||||
getChildActiveState = userInterface_scrollbar(
|
||||
uiState, assetManager, renderer, input, childUi->id,
|
||||
childUi->rect, &childUi->value, childUi->maxValue);
|
||||
break;
|
||||
|
||||
case uitype_textField:
|
||||
getChildActiveState = userInterface_textField(
|
||||
uiState, arena, assetManager, renderer, font, input,
|
||||
childUi->id, childUi->rect, childUi->string);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_LOG(
|
||||
"userInterface_window() warning: Enum uitype unrecognised");
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE(doyle): Capture only the first active id, but keep loop going to
|
||||
// render the rest of the window ui
|
||||
if (firstActiveChildId == -1 && getChildActiveState > 0)
|
||||
firstActiveChildId = getChildActiveState;
|
||||
}
|
||||
|
||||
if (firstActiveChildId != -1)
|
||||
return firstActiveChildId;
|
||||
|
||||
// NOTE(doyle): activeItem captures mouse click within the UI bounds, but if
|
||||
// the user drags the mouse outside the bounds quicker than the game updates
|
||||
// then we use a second flag which only "unclicks" when the mouse is let go
|
||||
if (uiState->activeItem == window->id) window->windowHeld = TRUE;
|
||||
|
||||
if (window->windowHeld)
|
||||
{
|
||||
if (!input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
window->windowHeld = FALSE;
|
||||
window->prevFrameWindowHeld = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (window->windowHeld)
|
||||
{
|
||||
// NOTE(doyle): If this is the first window click we don't process
|
||||
// movement and store the current position to delta from next cycle
|
||||
if (window->prevFrameWindowHeld)
|
||||
{
|
||||
// NOTE(doyle): Window clicked and held
|
||||
v2 deltaP = v2_sub(input.mouseP, window->prevMouseP);
|
||||
|
||||
for (i32 i = 0; i < window->numChildUiItems; i++)
|
||||
{
|
||||
UiItem *childUi = &window->childUiItems[i];
|
||||
childUi->rect.pos = v2_add(deltaP, childUi->rect.pos);
|
||||
}
|
||||
|
||||
DEBUG_PUSH_VAR("Delta Pos %4.2f, %4.2f", deltaP, "v2");
|
||||
window->rect.pos = v2_add(deltaP, window->rect.pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
window->prevFrameWindowHeld = TRUE;
|
||||
}
|
||||
|
||||
window->prevMouseP = input.mouseP;
|
||||
}
|
||||
|
||||
|
||||
if (!input.keys[keycode_mouseLeft].endedDown &&
|
||||
uiState->hotItem == window->id &&
|
||||
uiState->activeItem == window->id)
|
||||
{
|
||||
return window->id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
2477
src/WorldTraveller.c
Normal file
2477
src/WorldTraveller.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,4 @@
|
||||
@echo OFF
|
||||
|
||||
REM Build tags file
|
||||
ctags -R
|
||||
|
||||
REM Check if build tool is on path
|
||||
REM >nul, 2>nul will remove the output text from the where command
|
||||
where cl.exe >nul 2>nul
|
||||
if %errorlevel%==1 call msvc86.bat
|
||||
|
||||
REM Drop compilation files into build folder
|
||||
IF NOT EXIST ..\bin mkdir ..\bin
|
||||
pushd ..\bin
|
||||
|
163
src/dengine.c
163
src/dengine.c
@ -1,10 +1,9 @@
|
||||
#include "Dengine/AssetManager.h"
|
||||
#include "Dengine/Asteroid.h"
|
||||
#include "Dengine/Common.h"
|
||||
#include "Dengine/Debug.h"
|
||||
#include "Dengine/Math.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
#include "Dengine/OpenGL.h"
|
||||
#include "Dengine/WorldTraveller.h"
|
||||
|
||||
INTERNAL inline void processKey(KeyState *state, int action)
|
||||
{
|
||||
@ -19,12 +18,10 @@ INTERNAL void keyCallback(GLFWwindow *window, int key, int scancode, int action,
|
||||
{
|
||||
GameState *game = CAST(GameState *)(glfwGetWindowUserPointer(window));
|
||||
|
||||
#if 1
|
||||
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
||||
{
|
||||
glfwSetWindowShouldClose(window, GL_TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (key)
|
||||
{
|
||||
@ -58,8 +55,7 @@ INTERNAL void keyCallback(GLFWwindow *window, int key, int scancode, int action,
|
||||
i32 offset = 0;
|
||||
if (key >= 'A' && key <= 'Z')
|
||||
{
|
||||
KeyState *leftShiftKey = &game->input.keys[keycode_leftShift];
|
||||
if (!common_isSet(leftShiftKey->flags, keystateflag_ended_down))
|
||||
if (!game->input.keys[keycode_leftShift].endedDown)
|
||||
offset = 'a' - 'A';
|
||||
}
|
||||
|
||||
@ -85,7 +81,7 @@ INTERNAL void mouseButtonCallback(GLFWwindow *window, int button, int action,
|
||||
{
|
||||
GameState *game = CAST(GameState *)(glfwGetWindowUserPointer(window));
|
||||
|
||||
switch (button)
|
||||
switch(button)
|
||||
{
|
||||
case GLFW_MOUSE_BUTTON_LEFT:
|
||||
processKey(&game->input.keys[keycode_mouseLeft], action);
|
||||
@ -99,96 +95,21 @@ INTERNAL void scrollCallback(GLFWwindow *window, double xOffset, double yOffset)
|
||||
{
|
||||
}
|
||||
|
||||
INTERNAL void setGlfwWindowHints()
|
||||
{
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
|
||||
}
|
||||
|
||||
i32 main(void)
|
||||
{
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
common_unitTest();
|
||||
#endif
|
||||
|
||||
/*
|
||||
**************************
|
||||
* INIT APPLICATION WINDOW
|
||||
**************************
|
||||
*/
|
||||
glfwInit();
|
||||
setGlfwWindowHints();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
|
||||
|
||||
OptimalArrayV2 vidList = {0};
|
||||
common_optimalArrayV2Create(&vidList);
|
||||
|
||||
i32 windowWidth = 0;
|
||||
i32 windowHeight = 0;
|
||||
{ // Query Computer Video Resolutions
|
||||
|
||||
i32 numMonitors;
|
||||
GLFWmonitor **monitors = glfwGetMonitors(&numMonitors);
|
||||
GLFWmonitor *primaryMonitor = monitors[0];
|
||||
|
||||
i32 numModes;
|
||||
const GLFWvidmode *modes = glfwGetVideoModes(primaryMonitor, &numModes);
|
||||
|
||||
i32 targetRefreshHz = 60;
|
||||
f32 targetWindowRatio = 16.0f / 9.0f;
|
||||
|
||||
i32 targetPixelDensity = 1280 * 720;
|
||||
i32 minPixelDensityDelta = 100000000;
|
||||
|
||||
printf("== Supported video modes ==\n");
|
||||
for (i32 i = 0; i < numModes; i++)
|
||||
{
|
||||
GLFWvidmode mode = modes[i];
|
||||
printf("width: %d, height: %d, rgb: %d, %d, %d, refresh: %d\n",
|
||||
mode.width, mode.height, mode.redBits, mode.greenBits,
|
||||
mode.blueBits, mode.refreshRate);
|
||||
|
||||
if (mode.refreshRate == targetRefreshHz)
|
||||
{
|
||||
i32 result = common_optimalArrayV2Push(
|
||||
&vidList, V2i(mode.width, mode.height));
|
||||
|
||||
if (result)
|
||||
{
|
||||
printf(
|
||||
"common_optimalArrayV2Push(): Failed error code %d\n",
|
||||
result);
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
}
|
||||
|
||||
f32 sizeRatio = (f32)mode.width / (f32)mode.height;
|
||||
f32 delta = targetWindowRatio - sizeRatio;
|
||||
if (delta < 0.1f)
|
||||
{
|
||||
i32 pixelDensity = mode.width * mode.height;
|
||||
i32 densityDelta = ABS((pixelDensity - targetPixelDensity));
|
||||
|
||||
if (densityDelta < minPixelDensityDelta)
|
||||
{
|
||||
minPixelDensityDelta = densityDelta;
|
||||
windowWidth = mode.width;
|
||||
windowHeight = mode.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("== ==\n");
|
||||
ASSERT(vidList.index > 0);
|
||||
}
|
||||
|
||||
if (windowWidth == 0 || windowHeight == 0)
|
||||
{
|
||||
// NOTE(doyle): In this case just fallback to some value we hope is safe
|
||||
windowWidth = 800;
|
||||
windowHeight = 600;
|
||||
}
|
||||
i32 windowWidth = 1600;
|
||||
i32 windowHeight = 900;
|
||||
|
||||
GLFWwindow *window =
|
||||
glfwCreateWindow(windowWidth, windowHeight, "Dengine", NULL, NULL);
|
||||
@ -221,7 +142,6 @@ i32 main(void)
|
||||
i32 frameBufferWidth, frameBufferHeight;
|
||||
glfwGetFramebufferSize(window, &frameBufferWidth, &frameBufferHeight);
|
||||
glViewport(0, 0, frameBufferWidth, frameBufferHeight);
|
||||
v2 windowSize = V2i(frameBufferWidth, frameBufferHeight);
|
||||
|
||||
glfwSetKeyCallback(window, keyCallback);
|
||||
glfwSetCursorPosCallback(window, mouseCallback);
|
||||
@ -241,8 +161,9 @@ i32 main(void)
|
||||
*******************
|
||||
*/
|
||||
Memory memory = {0};
|
||||
MemoryIndex persistentSize = MEGABYTES(32);
|
||||
MemoryIndex transientSize = MEGABYTES(64);
|
||||
|
||||
size_t persistentSize = MEGABYTES(128);
|
||||
size_t transientSize = MEGABYTES(128);
|
||||
|
||||
memory.persistentSize = persistentSize;
|
||||
memory.persistent = PLATFORM_MEM_ALLOC_(NULL, persistentSize, u8);
|
||||
@ -250,27 +171,15 @@ i32 main(void)
|
||||
memory.transientSize = transientSize;
|
||||
memory.transient = PLATFORM_MEM_ALLOC_(NULL, transientSize, u8);
|
||||
|
||||
MemoryArena_ gameArena = {0};
|
||||
memory_arenaInit(&gameArena, memory.persistent, memory.persistentSize);
|
||||
GameState worldTraveller = {0};
|
||||
worldTraveller_gameInit(&worldTraveller,
|
||||
V2i(frameBufferWidth, frameBufferHeight), &memory);
|
||||
#ifdef DENGINE_DEBUG
|
||||
debug_init(&worldTraveller.arena_, V2i(windowWidth, windowHeight),
|
||||
worldTraveller.assetManager.font);
|
||||
#endif
|
||||
|
||||
GameState *gameState = MEMORY_PUSH_STRUCT(&gameArena, GameState);
|
||||
gameState->persistentArena = gameArena;
|
||||
|
||||
glfwSetWindowUserPointer(window, CAST(void *)(gameState));
|
||||
|
||||
{ // Load game icon
|
||||
i32 width, height;
|
||||
char *iconPath = "data/textures/Asteroids/icon.png";
|
||||
u8 *pixels = asset_imageLoad(&width, &height, NULL, iconPath, FALSE);
|
||||
|
||||
if (pixels)
|
||||
{
|
||||
GLFWimage image = {width, height, pixels};
|
||||
glfwSetWindowIcon(window, 1, &image);
|
||||
asset_imageFree(pixels);
|
||||
}
|
||||
}
|
||||
gameState->input.resolutionList = &vidList;
|
||||
glfwSetWindowUserPointer(window, CAST(void *)(&worldTraveller));
|
||||
|
||||
/*
|
||||
*******************
|
||||
@ -282,6 +191,7 @@ i32 main(void)
|
||||
|
||||
#if 0
|
||||
// TODO(doyle): Get actual monitor refresh rate
|
||||
i32 monitorRefreshHz = 60;
|
||||
f32 targetSecondsPerFrame = 1.0f / CAST(f32)(monitorRefreshHz);
|
||||
#else
|
||||
// TODO(doyle): http://gafferongames.com/game-physics/fix-your-timestep/
|
||||
@ -296,17 +206,16 @@ i32 main(void)
|
||||
glfwPollEvents();
|
||||
|
||||
/* Rendering commands here*/
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
asteroid_gameUpdateAndRender(gameState, &memory, windowSize,
|
||||
secondsElapsed);
|
||||
worldTraveller_gameUpdateAndRender(&worldTraveller, secondsElapsed);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
/* Swap the buffers */
|
||||
glfwSwapBuffers(window);
|
||||
|
||||
f32 endTime = CAST(f32) glfwGetTime();
|
||||
f32 endTime = CAST(f32)glfwGetTime();
|
||||
secondsElapsed = endTime - startTime;
|
||||
|
||||
#if 0
|
||||
@ -326,11 +235,9 @@ i32 main(void)
|
||||
f32 msPerFrame = secondsElapsed * 1000.0f;
|
||||
f32 framesPerSecond = 1.0f / secondsElapsed;
|
||||
|
||||
i32 entityCount = 0;
|
||||
GameWorldState *world =
|
||||
ASTEROID_GET_STATE_DATA(gameState, GameWorldState);
|
||||
|
||||
if (world) entityCount = world->entityIndex;
|
||||
i32 entityCount =
|
||||
worldTraveller.world[worldTraveller.currWorldIndex]
|
||||
.freeEntityIndex;
|
||||
|
||||
char textBuffer[256];
|
||||
snprintf(textBuffer, ARRAY_COUNT(textBuffer),
|
||||
@ -342,22 +249,6 @@ i32 main(void)
|
||||
}
|
||||
|
||||
startTime = endTime;
|
||||
|
||||
StartMenuState *menuState =
|
||||
ASTEROID_GET_STATE_DATA(gameState, StartMenuState);
|
||||
if (menuState)
|
||||
{
|
||||
if (menuState->newResolutionRequest)
|
||||
{
|
||||
i32 index = menuState->resStringDisplayIndex;
|
||||
windowSize = gameState->input.resolutionList->ptr[index];
|
||||
|
||||
glfwSetWindowSize(window, (i32)windowSize.w, (i32)windowSize.h);
|
||||
glViewport(0, 0, (i32)windowSize.w, (i32)windowSize.h);
|
||||
|
||||
menuState->newResolutionRequest = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glfwTerminate();
|
||||
|
@ -2,6 +2,8 @@
|
||||
#define DENGINE_ASSET_MANAGER_H
|
||||
|
||||
#include "Dengine/Assets.h"
|
||||
#include "Dengine/Shader.h"
|
||||
#include "Dengine/Texture.h"
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct MemoryArena MemoryArena_;
|
||||
@ -16,79 +18,69 @@ typedef struct AssetManager
|
||||
HashTable audio;
|
||||
|
||||
/* Primitive Array */
|
||||
u32 shaders[shaderlist_count];
|
||||
FontPack fontPack[4];
|
||||
i32 fontPackIndex;
|
||||
|
||||
Shader shaders[2];
|
||||
Font font;
|
||||
} AssetManager;
|
||||
|
||||
#define MAX_TEXTURE_SIZE 1024
|
||||
|
||||
void asset_init(AssetManager *assetManager, MemoryArena_ *arena);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Texture Managing
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const SubTexture asset_atlasGetSubTex(TexAtlas *const atlas,
|
||||
/*
|
||||
*********************************
|
||||
* Texture Operations
|
||||
*********************************
|
||||
*/
|
||||
const SubTexture asset_getAtlasSubTex(TexAtlas *const atlas,
|
||||
const char *const key);
|
||||
Texture *asset_texGet(AssetManager *const assetManager, const char *const key);
|
||||
TexAtlas *asset_atlasGetFreeSlot(AssetManager *const assetManager,
|
||||
Texture *asset_getTex(AssetManager *const assetManager, const char *const key);
|
||||
TexAtlas *asset_getFreeTexAtlasSlot(AssetManager *const assetManager,
|
||||
MemoryArena_ *arena, const char *const key,
|
||||
i32 numSubTex);
|
||||
TexAtlas *asset_atlasGet(AssetManager *const assetManager,
|
||||
TexAtlas *asset_getTexAtlas(AssetManager *const assetManager,
|
||||
const char *const key);
|
||||
Texture *asset_texGetFreeSlot(AssetManager *const assetManager,
|
||||
Texture *asset_getFreeTexSlot(AssetManager *const assetManager,
|
||||
MemoryArena_ *const arena, const char *const key);
|
||||
Texture *asset_texLoadImage(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
Texture *asset_loadTextureImage(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const char *const path, const char *const key);
|
||||
|
||||
u8 *asset_imageLoad(i32 *width, i32 *height, i32 *bpp, const char *const path,
|
||||
b32 flipVertically);
|
||||
|
||||
void asset_imageFree(u8 *image);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Animation Asset Managing
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void asset_animAdd(AssetManager *const assetManager, MemoryArena_ *const arena,
|
||||
const char *const animName, TexAtlas *const atlas,
|
||||
char **const subTextureNames, const i32 numSubTextures,
|
||||
const f32 frameDuration);
|
||||
Animation *asset_animGet(AssetManager *const assetManager,
|
||||
/*
|
||||
*********************************
|
||||
* Animation Asset Managing
|
||||
*********************************
|
||||
*/
|
||||
void asset_addAnimation(AssetManager *const assetManager,
|
||||
MemoryArena_ *const arena, const char *const animName,
|
||||
TexAtlas *const atlas, char **const subTextureNames,
|
||||
const i32 numSubTextures, const f32 frameDuration);
|
||||
Animation *asset_getAnim(AssetManager *const assetManager,
|
||||
const char *const key);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Audio
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
AudioVorbis *const asset_vorbisGet(AssetManager *const assetManager,
|
||||
/*
|
||||
*********************************
|
||||
* Audio
|
||||
*********************************
|
||||
*/
|
||||
AudioVorbis *const asset_getVorbis(AssetManager *const assetManager,
|
||||
const char *const key);
|
||||
const i32 asset_vorbisLoad(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const char *const path, const char *const key);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Everything else
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const i32 asset_xmlLoad(AssetManager *const assetManager,
|
||||
/*
|
||||
*********************************
|
||||
* Everything else
|
||||
*********************************
|
||||
*/
|
||||
const i32 asset_loadXmlFile(AssetManager *const assetManager,
|
||||
MemoryArena_ *const arena,
|
||||
const PlatformFileRead *const fileRead);
|
||||
|
||||
u32 asset_shaderGet(AssetManager *assetManager, const enum ShaderList type);
|
||||
const i32 asset_shaderLoad(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
Shader *const asset_getShader(AssetManager *assetManager, const enum ShaderList type);
|
||||
const i32 asset_loadShaderFiles(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const char *const vertexPath,
|
||||
const char *const fragmentPath,
|
||||
const enum ShaderList type);
|
||||
|
||||
Font *asset_fontGetOrCreateOnDemand(AssetManager *assetManager,
|
||||
MemoryArena_ *persistentArena,
|
||||
MemoryArena_ *transientArena, char *name,
|
||||
i32 size);
|
||||
Font *asset_fontGet(AssetManager *assetManager, char *name, i32 size);
|
||||
const i32 asset_fontLoadTTF(AssetManager *assetManager,
|
||||
MemoryArena_ *persistentArena,
|
||||
MemoryArena_ *transientArena, char *filePath,
|
||||
char *name, i32 targetFontHeight);
|
||||
|
||||
const v2 asset_fontStringDimInPixels(const Font *const font,
|
||||
const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena_ *arena,
|
||||
const char *filePath);
|
||||
const v2 asset_stringDimInPixels(const Font *const font,
|
||||
const char *const string);
|
||||
|
||||
void asset_unitTest(MemoryArena_ *arena);
|
||||
|
@ -44,8 +44,7 @@ typedef struct XmlNode
|
||||
|
||||
enum ShaderList
|
||||
{
|
||||
shaderlist_default,
|
||||
shaderlist_default_no_tex,
|
||||
shaderlist_sprite,
|
||||
shaderlist_count,
|
||||
};
|
||||
|
||||
@ -95,6 +94,7 @@ typedef struct AudioVorbis
|
||||
* Texture Assets
|
||||
*********************************
|
||||
*/
|
||||
|
||||
typedef struct SubTexture
|
||||
{
|
||||
Rect rect;
|
||||
@ -123,34 +123,6 @@ typedef struct Animation
|
||||
f32 frameDuration;
|
||||
} Animation;
|
||||
|
||||
#define TARGET_TEXTURE_SIZE 1024
|
||||
#define TARGET_BYTES_PER_PIXEL 4
|
||||
|
||||
// TODO(doyle): Look into merging into assets.h file ..
|
||||
typedef struct Texture
|
||||
{
|
||||
// Holds the ID of the texture object, used for all texture operations to
|
||||
// reference to this particlar texture
|
||||
u32 id;
|
||||
|
||||
// Texture image dimensions
|
||||
u32 width;
|
||||
u32 height;
|
||||
|
||||
// Texture Format
|
||||
u32 internalFormat; // Format of texture object
|
||||
u32 imageFormat; // Format of loaded image
|
||||
|
||||
// Texture configuration
|
||||
u32 wrapS; // Wrapping mode on S axis
|
||||
u32 wrapT; // Wrapping mode on T axis
|
||||
|
||||
// Filtering mode if texture pixels < screen pixels
|
||||
u32 filterMinification;
|
||||
// Filtering mode if texture pixels > screen pixels
|
||||
u32 filterMagnification;
|
||||
} Texture;
|
||||
|
||||
/*
|
||||
*********************************
|
||||
* Font
|
||||
@ -176,13 +148,6 @@ typedef struct CharMetrics
|
||||
|
||||
typedef struct Font
|
||||
{
|
||||
// NOTE(doyle): In stb_font we scale the loaded font to this target height
|
||||
// and so this is used as a general font "size" notion
|
||||
union {
|
||||
i32 fontHeight;
|
||||
i32 size;
|
||||
};
|
||||
|
||||
TexAtlas *atlas;
|
||||
FontMetrics metrics;
|
||||
|
||||
@ -194,14 +159,6 @@ typedef struct Font
|
||||
|
||||
v2 codepointRange;
|
||||
v2 maxSize;
|
||||
} Font;
|
||||
|
||||
// NOTE(doyle): A font pack is a singular font at different sizes
|
||||
typedef struct FontPack
|
||||
{
|
||||
char *name;
|
||||
char *filePath;
|
||||
Font font[4];
|
||||
i32 fontIndex;
|
||||
} FontPack;
|
||||
} Font;
|
||||
#endif
|
||||
|
@ -1,114 +0,0 @@
|
||||
#ifndef ASTEROID_H
|
||||
#define ASTEROID_H
|
||||
|
||||
#include "Dengine/AssetManager.h"
|
||||
#include "Dengine/Audio.h"
|
||||
#include "Dengine/Common.h"
|
||||
#include "Dengine/Entity.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
#include "Dengine/Platform.h"
|
||||
#include "Dengine/Renderer.h"
|
||||
#include "Dengine/Ui.h"
|
||||
|
||||
enum AppState
|
||||
{
|
||||
appstate_StartMenuState,
|
||||
appstate_GameWorldState,
|
||||
appstate_count,
|
||||
};
|
||||
|
||||
enum GameWorldStateFlags
|
||||
{
|
||||
gameworldstateflags_init = (1 << 0),
|
||||
gameworldstateflags_level_started = (1 << 1),
|
||||
gameworldstateflags_player_lost = (1 << 2),
|
||||
gameworldstateflags_create_player = (1 << 3),
|
||||
};
|
||||
|
||||
typedef struct GameWorldState
|
||||
{
|
||||
enum GameWorldStateFlags flags;
|
||||
|
||||
MemoryArena_ entityArena;
|
||||
|
||||
v2 *entityVertexListCache[entitytype_count];
|
||||
Entity *entityList;
|
||||
i32 entityListSize;
|
||||
i32 entityIndex;
|
||||
u32 entityIdCounter;
|
||||
|
||||
u32 asteroidCounter;
|
||||
u32 numAsteroids;
|
||||
v2 *asteroidSmallVertexCache[3];
|
||||
v2 *asteroidMediumVertexCache[3];
|
||||
v2 *asteroidLargeVertexCache[3];
|
||||
|
||||
v2 *bulletVertexCache;
|
||||
v2 *particleVertexCache;
|
||||
|
||||
v2 *starPList;
|
||||
f32 starOpacity;
|
||||
f32 starMinOpacity;
|
||||
b32 starFadeAway;
|
||||
i32 numStarP;
|
||||
|
||||
// TODO(doyle): Audio mixing instead of multiple renderers
|
||||
AudioRenderer *audioRenderer;
|
||||
i32 numAudioRenderers;
|
||||
|
||||
f32 pixelsPerMeter;
|
||||
Rect camera;
|
||||
v2 size;
|
||||
|
||||
// TODO(doyle): Ensure we change this if it gets too big
|
||||
b32 collisionTable[entitytype_count][entitytype_count];
|
||||
|
||||
i32 score;
|
||||
i32 scoreMultiplier;
|
||||
f32 scoreMultiplierBarTimer;
|
||||
f32 scoreMultiplierBarThresholdInS;
|
||||
|
||||
f32 timeSinceLastShot;
|
||||
|
||||
} GameWorldState;
|
||||
|
||||
typedef struct StartMenuState
|
||||
{
|
||||
b32 init;
|
||||
|
||||
f32 startPromptBlinkTimer;
|
||||
b32 startPromptShow;
|
||||
|
||||
char **resStrings;
|
||||
i32 numResStrings;
|
||||
i32 resStringDisplayIndex;
|
||||
|
||||
b32 newResolutionRequest;
|
||||
|
||||
b32 optionsShow;
|
||||
} StartMenuState;
|
||||
|
||||
typedef struct GameState
|
||||
{
|
||||
b32 init;
|
||||
enum AppState currState;
|
||||
void *appStateData[appstate_count];
|
||||
|
||||
MemoryArena_ transientArena;
|
||||
MemoryArena_ persistentArena;
|
||||
|
||||
AudioManager audioManager;
|
||||
AssetManager assetManager;
|
||||
InputBuffer input;
|
||||
Renderer renderer;
|
||||
|
||||
UiState uiState;
|
||||
} GameState;
|
||||
|
||||
#define ASTEROID_GET_STATE_DATA(state, type) (type *)asteroid_getStateData_(state, appstate_##type)
|
||||
void *asteroid_getStateData_(GameState *state, enum AppState appState);
|
||||
|
||||
void asteroid_gameUpdateAndRender(GameState *state, Memory *memory,
|
||||
v2 windowSize, f32 dt);
|
||||
|
||||
#endif
|
@ -49,17 +49,17 @@ typedef struct AudioRenderer
|
||||
|
||||
|
||||
const i32 audio_init(AudioManager *audioManager);
|
||||
const i32 audio_vorbisPlay(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
const i32 audio_playVorbis(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer, AudioVorbis *vorbis,
|
||||
i32 numPlays);
|
||||
const i32 audio_vorbisStream(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
const i32 audio_streamPlayVorbis(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer,
|
||||
AudioVorbis *vorbis, i32 numPlays);
|
||||
const i32 audio_vorbisStop(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
const i32 audio_stopVorbis(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer);
|
||||
const i32 audio_vorbisPause(AudioManager *audioManager,
|
||||
const i32 audio_pauseVorbis(AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer);
|
||||
const i32 audio_vorbisResume(AudioManager *audioManager,
|
||||
const i32 audio_resumeVorbis(AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer);
|
||||
const i32 audio_updateAndPlay(MemoryArena_ *arena, AudioManager *audioManager,
|
||||
AudioRenderer *audioRenderer);
|
||||
|
@ -14,11 +14,6 @@ typedef i32 b32;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
typedef size_t MemoryIndex;
|
||||
typedef char String;
|
||||
|
||||
typedef struct MemoryArena MemoryArena_;
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
@ -37,99 +32,16 @@ typedef struct MemoryArena MemoryArena_;
|
||||
|
||||
#define DENGINE_DEBUG
|
||||
|
||||
#include "Dengine/Math.h"
|
||||
|
||||
/*
|
||||
NOTE(doyle): Small sized optimised dynamic array that grows as required. The
|
||||
array uses the stack first, only if it runs out of space does it rely on
|
||||
memory allocated from the machine.
|
||||
|
||||
The array->ptr is initially set to fast storage. Once we are out of space
|
||||
we allocate space on the heap for the ptr and copy over the elements in
|
||||
fast storage.
|
||||
|
||||
The default behaviour expands the array storage by the size of fastStorage.
|
||||
*/
|
||||
|
||||
enum OptimalArrayError
|
||||
{
|
||||
optimalarrayerror_out_of_memory = 1,
|
||||
optimalarrayerror_count,
|
||||
};
|
||||
|
||||
typedef struct OptimalArrayV2
|
||||
{
|
||||
v2 fastStorage[16];
|
||||
v2 *ptr;
|
||||
i32 index;
|
||||
i32 size;
|
||||
} OptimalArrayV2;
|
||||
void common_optimalArrayV2Create(OptimalArrayV2 *array);
|
||||
i32 common_optimalArrayV2Push(OptimalArrayV2 *array, v2 data);
|
||||
void common_optimalArrayV2Destroy(OptimalArrayV2 *array);
|
||||
|
||||
i32 common_stringLen(String *const string);
|
||||
String *const common_stringAppend(MemoryArena_ *const arena, String *oldString,
|
||||
String *appendString, i32 appendLen);
|
||||
void common_stringFree(MemoryArena_ *arena, String *string);
|
||||
String *const common_stringMake(MemoryArena_ *const arena, char *string);
|
||||
String *const common_stringMakeLen(MemoryArena_ *const arena, i32 len);
|
||||
|
||||
i32 common_strlen(const char *const string);
|
||||
i32 common_strcmp(const char *a, const char *b);
|
||||
void common_strncat(char *dest, const char *src, i32 numChars);
|
||||
char *common_strncpy(char *dest, const char *src, i32 numChars);
|
||||
|
||||
u8 *common_memset(u8 *const ptr, const i32 value, const i32 numBytes);
|
||||
|
||||
// Max buffer size should be 11 for 32 bit integers
|
||||
#define COMMON_ITOA_MAX_BUFFER_32BIT 11
|
||||
void common_itoa(i32 value, char *buf, i32 bufSize);
|
||||
i32 common_atoi(const char *string, const i32 len);
|
||||
|
||||
inline b32 common_isSet(u32 bitfield, u32 flags)
|
||||
{
|
||||
b32 result = FALSE;
|
||||
if ((bitfield & flags) == flags) result = TRUE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void common_unitTest()
|
||||
{
|
||||
{
|
||||
char itoa[COMMON_ITOA_MAX_BUFFER_32BIT] = {0};
|
||||
common_itoa(0, itoa, ARRAY_COUNT(itoa));
|
||||
ASSERT(itoa[0] == '0');
|
||||
ASSERT(itoa[1] == 0);
|
||||
|
||||
common_memset(itoa, 1, ARRAY_COUNT(itoa));
|
||||
for (i32 i = 0; i < ARRAY_COUNT(itoa); i++) ASSERT(itoa[i] == 1);
|
||||
|
||||
common_memset(itoa, 0, ARRAY_COUNT(itoa));
|
||||
for (i32 i = 0; i < ARRAY_COUNT(itoa); i++) ASSERT(itoa[i] == 0);
|
||||
|
||||
common_itoa(-123, itoa, ARRAY_COUNT(itoa));
|
||||
ASSERT(itoa[0] == '-');
|
||||
ASSERT(itoa[1] == '1');
|
||||
ASSERT(itoa[2] == '2');
|
||||
ASSERT(itoa[3] == '3');
|
||||
ASSERT(itoa[4] == 0);
|
||||
|
||||
common_memset(itoa, 0, ARRAY_COUNT(itoa));
|
||||
for (i32 i = 0; i < ARRAY_COUNT(itoa); i++) ASSERT(itoa[i] == 0);
|
||||
common_itoa(93, itoa, ARRAY_COUNT(itoa));
|
||||
ASSERT(itoa[0] == '9');
|
||||
ASSERT(itoa[1] == '3');
|
||||
ASSERT(itoa[2] == 0);
|
||||
|
||||
common_memset(itoa, 0, ARRAY_COUNT(itoa));
|
||||
for (i32 i = 0; i < ARRAY_COUNT(itoa); i++) ASSERT(itoa[i] == 0);
|
||||
common_itoa(-0, itoa, ARRAY_COUNT(itoa));
|
||||
ASSERT(itoa[0] == '0');
|
||||
ASSERT(itoa[1] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash2, by Austin Appleby
|
||||
|
@ -20,13 +20,13 @@ enum DebugCount
|
||||
debugcount_num,
|
||||
};
|
||||
|
||||
void debug_init(v2 windowSize, Font font);
|
||||
void debug_init(MemoryArena_ *arena, v2 windowSize, Font font);
|
||||
|
||||
#define DEBUG_RECURSIVE_PRINT_XML_TREE(sig) debug_recursivePrintXmlTree(sig, 1)
|
||||
void debug_recursivePrintXmlTree(XmlNode *root, i32 levelsDeep);
|
||||
|
||||
void debug_countIncrement(enum DebugCount id);
|
||||
|
||||
void debug_clearCounter();
|
||||
#define DEBUG_LOG(string) debug_consoleLog(string, __FILE__, __LINE__);
|
||||
void debug_consoleLog(char *string, char *file, int lineNum);
|
||||
|
||||
|
@ -1,69 +1,81 @@
|
||||
#ifndef ENTITY_H
|
||||
#define ENTITY_H
|
||||
#ifndef DENGINE_ENTITY_H
|
||||
#define DENGINE_ENTITY_H
|
||||
|
||||
#include "Dengine/Assets.h"
|
||||
#include "Dengine/Common.h"
|
||||
#include "Dengine/Renderer.h"
|
||||
#include "Dengine/Math.h"
|
||||
#include "Dengine/Assets.h"
|
||||
|
||||
typedef struct AssetManager AssetManager;
|
||||
typedef struct AudioRenderer AudioRenderer;
|
||||
typedef struct MemoryArena_ MemoryArena;
|
||||
typedef struct MemoryArena MemoryArena_;
|
||||
typedef struct World World;
|
||||
typedef struct EventQueue EventQueue;
|
||||
|
||||
typedef struct Entity Entity;
|
||||
|
||||
enum Direction
|
||||
{
|
||||
direction_north,
|
||||
direction_northwest,
|
||||
direction_west,
|
||||
direction_southwest,
|
||||
direction_south,
|
||||
direction_southeast,
|
||||
direction_east,
|
||||
direction_northeast,
|
||||
direction_count,
|
||||
direction_null,
|
||||
direction_num,
|
||||
direction_null,
|
||||
};
|
||||
|
||||
INTERNAL inline enum Direction invertDirection(enum Direction direction)
|
||||
{
|
||||
enum Direction result = (direction + 4) % direction_count;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline enum Direction rotateDirectionWest90(enum Direction direction)
|
||||
{
|
||||
enum Direction result = (direction + 2) % direction_count;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline enum Direction rotateDirectionEast90(enum Direction direction)
|
||||
{
|
||||
enum Direction result = (direction + 2) % direction_count;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#define NULL_ENTITY_ID 0
|
||||
enum EntityType
|
||||
{
|
||||
entitytype_invalid,
|
||||
entitytype_null,
|
||||
entitytype_ship,
|
||||
|
||||
// NOTE(doyle): Asteroids must be grouped since we use >= and <= logic to
|
||||
// combine asteroid logic together
|
||||
entitytype_asteroid_small,
|
||||
entitytype_asteroid_medium,
|
||||
entitytype_asteroid_large,
|
||||
|
||||
entitytype_particle,
|
||||
|
||||
entitytype_bullet,
|
||||
entitytype_hero,
|
||||
entitytype_weapon,
|
||||
entitytype_projectile,
|
||||
entitytype_attackObject,
|
||||
entitytype_npc,
|
||||
entitytype_mob,
|
||||
entitytype_tile,
|
||||
entitytype_soundscape,
|
||||
entitytype_count,
|
||||
};
|
||||
|
||||
enum EntityState
|
||||
{
|
||||
entitystate_idle,
|
||||
entitystate_battle,
|
||||
entitystate_attack,
|
||||
entitystate_dead,
|
||||
entitystate_count,
|
||||
entitystate_invalid,
|
||||
};
|
||||
|
||||
enum EntityAttack
|
||||
{
|
||||
entityattack_claudeAttack,
|
||||
entityattack_claudeAttackUp,
|
||||
entityattack_claudeAttackDown,
|
||||
entityattack_claudeDragonHowl,
|
||||
entityattack_claudeEnergySword,
|
||||
entityattack_claudeRipperBlast,
|
||||
entityattack_claudeAirSlash,
|
||||
entityattack_count,
|
||||
entityattack_invalid,
|
||||
};
|
||||
|
||||
typedef struct EntityStats
|
||||
{
|
||||
f32 maxHealth;
|
||||
f32 health;
|
||||
|
||||
f32 actionRate;
|
||||
f32 actionTimer;
|
||||
f32 actionSpdMul;
|
||||
|
||||
f32 busyDuration;
|
||||
i32 entityIdToAttack;
|
||||
enum EntityAttack queuedAttack;
|
||||
|
||||
Entity *weapon;
|
||||
} EntityStats;
|
||||
|
||||
typedef struct EntityAnim
|
||||
{
|
||||
Animation *anim;
|
||||
@ -73,49 +85,63 @@ typedef struct EntityAnim
|
||||
u32 timesCompleted;
|
||||
} EntityAnim;
|
||||
|
||||
typedef struct Entity
|
||||
struct Entity
|
||||
{
|
||||
u32 id;
|
||||
i32 id;
|
||||
|
||||
v2 pos;
|
||||
v2 dP;
|
||||
v2 particleInitDp;
|
||||
i32 childIds[8];
|
||||
i32 numChilds;
|
||||
|
||||
v2 pos; // Position
|
||||
v2 dPos; // Velocity
|
||||
v2 hitbox;
|
||||
v2 size;
|
||||
|
||||
/*
|
||||
NOTE(doyle): Offset is the vector to shift the polygons "origin" to the
|
||||
world origin. In our case this should strictly be negative as we have
|
||||
a requirement that all vertex points must be strictly positive.
|
||||
*/
|
||||
v2 offset;
|
||||
|
||||
enum RenderMode renderMode;
|
||||
i32 numVertexPoints;
|
||||
v2 *vertexPoints;
|
||||
|
||||
f32 scale;
|
||||
Degrees rotation;
|
||||
f32 rotation;
|
||||
|
||||
b32 invisible;
|
||||
|
||||
enum EntityState state;
|
||||
enum EntityType type;
|
||||
enum Direction direction;
|
||||
|
||||
v4 color;
|
||||
Texture *tex;
|
||||
b32 flipX;
|
||||
b32 flipY;
|
||||
|
||||
// TODO(doyle): Two collision flags, we want certain entities to collide
|
||||
// with certain types of entities only (i.e. projectile from hero to enemy,
|
||||
// only collides with enemy). Having two flags is redundant, but! it does
|
||||
// allow for early-exit in collision check if the entity doesn't collide at
|
||||
// all
|
||||
b32 collides;
|
||||
enum EntityType collidesWith[entitytype_count];
|
||||
|
||||
EntityAnim animList[16];
|
||||
i32 animListIndex;
|
||||
} Entity;
|
||||
i32 currAnimId;
|
||||
|
||||
SubTexture entity_subTexGetCurr(Entity *const entity);
|
||||
void entity_animSet(Entity *const entity, const char *const animName);
|
||||
void entity_animUpdate(Entity *const entity, const f32 dt);
|
||||
void entity_animAdd(AssetManager *const assetManager, Entity *const entity,
|
||||
EntityStats *stats;
|
||||
|
||||
// TODO(doyle): Audio mixing instead of multiple renderers
|
||||
AudioRenderer *audioRenderer;
|
||||
i32 numAudioRenderers;
|
||||
};
|
||||
|
||||
SubTexture entity_getActiveSubTexture(Entity *const entity);
|
||||
void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity,
|
||||
const char *const animName);
|
||||
|
||||
v2 *entity_generateUpdatedVertexList(MemoryArena_ *transientArena,
|
||||
Entity *entity);
|
||||
void entity_updateAnim(EventQueue *eventQueue, Entity *const entity,
|
||||
const f32 dt);
|
||||
void entity_addAnim(AssetManager *const assetManager, Entity *const entity,
|
||||
const char *const animName);
|
||||
Entity *const entity_add(MemoryArena_ *const arena, World *const world,
|
||||
const v2 pos, const v2 size, const f32 scale,
|
||||
const enum EntityType type,
|
||||
const enum Direction direction, Texture *const tex,
|
||||
const b32 collides);
|
||||
void entity_clearData(MemoryArena_ *const arena, World *const world,
|
||||
Entity *const entity);
|
||||
Entity *entity_get(World *const world, const i32 entityId);
|
||||
i32 entity_getIndex(World *const world, const i32 entityId);
|
||||
#endif
|
||||
|
@ -2,58 +2,32 @@
|
||||
#define DENGINE_MATH_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "Dengine/Common.h"
|
||||
|
||||
#define MATH_PI 3.14159265359f
|
||||
#define SQUARED(x) (x * x)
|
||||
#define ABS(x) ((x) > 0 ? (x) : -(x))
|
||||
#define DEGREES_TO_RADIANS(x) ((x * (MATH_PI / 180.0f)))
|
||||
#define RADIANS_TO_DEGREES(x) ((x * (180.0f / MATH_PI)))
|
||||
#define MAX(a, b) ((a) < (b) ? (b) : (a))
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define SQUARED(x) ((x) * (x))
|
||||
#define DEGREES_TO_RADIANS(x) (x * (MATH_PI / 180.0f))
|
||||
#define RADIANS_TO_DEGREES(x) (x * (180.0f / MATH_PI))
|
||||
#define SQRT(x) (sqrtf(x))
|
||||
|
||||
typedef f32 Radians;
|
||||
typedef f32 Degrees;
|
||||
|
||||
INTERNAL inline f32 math_acosf(f32 a)
|
||||
{
|
||||
f32 result = acosf(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline Radians math_cosf(Radians a)
|
||||
{
|
||||
Radians result = cosf(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline Radians math_sinf(Radians a)
|
||||
{
|
||||
Radians result = sinf(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline f32 math_atan2f(f32 y, f32 x)
|
||||
{
|
||||
f32 result = atan2f(y, x);
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline f32 math_tanf(Radians angle)
|
||||
{
|
||||
f32 result = tanf(angle);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* VECTORS */
|
||||
typedef union v2
|
||||
{
|
||||
struct { f32 x, y; };
|
||||
struct { f32 w, h; };
|
||||
struct { f32 min, max; };
|
||||
f32 e[2];
|
||||
} v2;
|
||||
|
||||
@ -74,8 +48,8 @@ typedef union v4
|
||||
|
||||
typedef struct Rect
|
||||
{
|
||||
v2 min;
|
||||
v2 max;
|
||||
v2 pos;
|
||||
v2 size;
|
||||
} Rect;
|
||||
|
||||
INTERNAL inline v2 V2i(const i32 x, const i32 y)
|
||||
@ -140,7 +114,7 @@ INTERNAL inline v##num v##num##_scale(const v##num a, const f32 b) \
|
||||
for (i32 i = 0; i < ##num; i++) { result.e[i] = a.e[i] * b; } \
|
||||
return result; \
|
||||
} \
|
||||
INTERNAL inline v##num v##num##_hadamard(const v##num a, const v##num b) \
|
||||
INTERNAL inline v##num v##num##_mul(const v##num a, const v##num b) \
|
||||
{ \
|
||||
v##num result; \
|
||||
for (i32 i = 0; i < ##num; i++) { result.e[i] = a.e[i] * b.e[i]; } \
|
||||
@ -194,34 +168,6 @@ INTERNAL inline v2 v2_normalise(const v2 a)
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline b32 v2_intervalsOverlap(v2 a, v2 b)
|
||||
{
|
||||
b32 result = FALSE;
|
||||
|
||||
f32 lenOfA = a.max - a.min;
|
||||
f32 lenOfB = b.max - b.min;
|
||||
|
||||
if (lenOfA > lenOfB)
|
||||
{
|
||||
v2 tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
if ((a.min >= b.min && a.min <= b.max) ||
|
||||
(a.max >= b.min && a.max <= b.max))
|
||||
{
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline v2 v2_perpendicular(const v2 a) {
|
||||
v2 result = {a.y, -a.x};
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline v3 v3_cross(const v3 a, const v3 b)
|
||||
{
|
||||
/*
|
||||
@ -332,7 +278,7 @@ INTERNAL inline mat4 mat4_mul(const mat4 a, const mat4 b)
|
||||
}
|
||||
|
||||
|
||||
INTERNAL inline v4 mat4_mulV4(const mat4 a, const v4 b)
|
||||
INTERNAL inline v4 mat4_mul_v4(const mat4 a, const v4 b)
|
||||
{
|
||||
v4 result = {0};
|
||||
|
||||
@ -348,14 +294,14 @@ INTERNAL inline v4 mat4_mulV4(const mat4 a, const v4 b)
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline b32 math_rectContainsP(Rect rect, v2 p)
|
||||
INTERNAL inline b32 math_pointInRect(Rect rect, v2 point)
|
||||
{
|
||||
b32 outsideOfRectX = FALSE;
|
||||
if (p.x < rect.min.x || p.x > rect.max.w)
|
||||
if (point.x < rect.pos.x || point.x > (rect.pos.x + rect.size.w))
|
||||
outsideOfRectX = TRUE;
|
||||
|
||||
b32 outsideOfRectY = FALSE;
|
||||
if (p.y < rect.min.y || p.y > rect.max.h)
|
||||
if (point.y < rect.pos.y || point.y > (rect.pos.y + rect.size.h))
|
||||
outsideOfRectY = TRUE;
|
||||
|
||||
if (outsideOfRectX || outsideOfRectY) return FALSE;
|
||||
@ -363,92 +309,22 @@ INTERNAL inline b32 math_rectContainsP(Rect rect, v2 p)
|
||||
}
|
||||
|
||||
|
||||
INTERNAL inline Rect math_rectCreate(v2 origin, v2 size)
|
||||
INTERNAL inline v4 math_getRect(v2 origin, v2 size)
|
||||
{
|
||||
Rect result = {0};
|
||||
result.min = origin;
|
||||
result.max = v2_add(origin, size);
|
||||
v2 upperRightBound = v2_add(origin, V2(size.x, size.y));
|
||||
v4 result = V4(origin.x, origin.y, upperRightBound.x, upperRightBound.y);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline v2 math_rectGetSize(Rect rect)
|
||||
INTERNAL inline v2 math_getRectSize(v4 rect)
|
||||
{
|
||||
f32 width = ABS(rect.max.x - rect.min.x);
|
||||
f32 height = ABS(rect.max.y - rect.min.y);
|
||||
f32 width = ABS(rect.x - rect.z);
|
||||
f32 height = ABS(rect.y - rect.w);
|
||||
|
||||
v2 result = V2(width, height);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline v2 math_rectGetCentre(Rect rect)
|
||||
{
|
||||
f32 sumX = rect.min.x + rect.max.x;
|
||||
f32 sumY = rect.min.y + rect.max.y;
|
||||
v2 result = v2_scale(V2(sumX, sumY), 0.5f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline Rect math_rectShift(Rect rect, v2 shift)
|
||||
{
|
||||
Rect result = {0};
|
||||
result.min = v2_add(rect.min, shift);
|
||||
result.max = v2_add(rect.max, shift);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL inline void math_applyRotationToVertexes(v2 pos, v2 pivotPoint,
|
||||
Radians rotate,
|
||||
v2 *vertexList,
|
||||
i32 vertexListSize)
|
||||
{
|
||||
if (rotate == 0) return;
|
||||
// NOTE(doyle): Move the world origin to the base position of the object.
|
||||
// Then move the origin to the pivot point (e.g. center of object) and
|
||||
// rotate from that point.
|
||||
v2 pointOfRotation = v2_add(pivotPoint, pos);
|
||||
|
||||
mat4 rotateMat = mat4_translate(pointOfRotation.x, pointOfRotation.y, 0.0f);
|
||||
rotateMat = mat4_mul(rotateMat, mat4_rotate(rotate, 0.0f, 0.0f, 1.0f));
|
||||
rotateMat = mat4_mul(rotateMat, mat4_translate(-pointOfRotation.x,
|
||||
-pointOfRotation.y, 0.0f));
|
||||
|
||||
// TODO(doyle): Make it nice for 2d vector, stop using mat4
|
||||
for (i32 i = 0; i < vertexListSize; i++)
|
||||
{
|
||||
// NOTE(doyle): Manual matrix multiplication since vertex pos is 2D and
|
||||
// matrix is 4D
|
||||
v2 oldP = vertexList[i];
|
||||
v2 newP = {0};
|
||||
|
||||
newP.x = (oldP.x * rotateMat.e[0][0]) + (oldP.y * rotateMat.e[1][0]) +
|
||||
(rotateMat.e[3][0]);
|
||||
newP.y = (oldP.x * rotateMat.e[0][1]) + (oldP.y * rotateMat.e[1][1]) +
|
||||
(rotateMat.e[3][1]);
|
||||
|
||||
vertexList[i] = newP;
|
||||
}
|
||||
}
|
||||
INTERNAL inline f32 math_lerp(f32 a, f32 t, f32 b)
|
||||
{
|
||||
/*
|
||||
Linear blend between two values. We having a starting point "a", and
|
||||
the distance to "b" is defined as (b - a). Then we can say
|
||||
|
||||
a + t(b - a)
|
||||
|
||||
As our linear blend fn. We start from "a" and choosing a t from 0->1
|
||||
will vary the value of (b - a) towards b. If we expand this, this
|
||||
becomes
|
||||
|
||||
a + (t * b) - (a * t) == (1 - t)a + t*b
|
||||
*/
|
||||
f32 result = ((1 - t) * a) + (t * b);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -5,36 +5,25 @@
|
||||
|
||||
typedef struct MemoryArena
|
||||
{
|
||||
MemoryIndex size;
|
||||
MemoryIndex used;
|
||||
size_t size;
|
||||
size_t used;
|
||||
u8 *base;
|
||||
|
||||
i32 tempMemoryCount;
|
||||
} MemoryArena_;
|
||||
|
||||
typedef struct Memory
|
||||
{
|
||||
void *persistent;
|
||||
MemoryIndex persistentSize;
|
||||
size_t persistentSize;
|
||||
|
||||
void *transient;
|
||||
MemoryIndex transientSize;
|
||||
size_t transientSize;
|
||||
|
||||
b32 init;
|
||||
} Memory;
|
||||
|
||||
typedef struct TempMemory
|
||||
{
|
||||
MemoryArena_ *arena;
|
||||
MemoryIndex used;
|
||||
} TempMemory;
|
||||
|
||||
TempMemory memory_beginTempRegion(MemoryArena_ *arena);
|
||||
void memory_endTempRegion(TempMemory tempMemory);
|
||||
|
||||
#define MEMORY_PUSH_STRUCT(arena, type) (type *)memory_pushBytes(arena, sizeof(type))
|
||||
#define MEMORY_PUSH_ARRAY(arena, count, type) (type *)memory_pushBytes(arena, (count)*sizeof(type))
|
||||
inline void *memory_pushBytes(MemoryArena_ *arena, MemoryIndex size)
|
||||
inline void *memory_pushBytes(MemoryArena_ *arena, size_t size)
|
||||
{
|
||||
ASSERT((arena->used + size) <= arena->size);
|
||||
void *result = arena->base + arena->used;
|
||||
@ -43,6 +32,6 @@ inline void *memory_pushBytes(MemoryArena_ *arena, MemoryIndex size)
|
||||
return result;
|
||||
}
|
||||
|
||||
void memory_arenaInit(MemoryArena_ *arena, void *base, MemoryIndex size);
|
||||
void memory_arenaInit(MemoryArena_ *arena, void *base, size_t size);
|
||||
|
||||
#endif
|
||||
|
@ -119,35 +119,19 @@ enum KeyCode
|
||||
keycode_null,
|
||||
};
|
||||
|
||||
/*
|
||||
NOTE(doyle): EndedDown describes the last state the key was in and is
|
||||
_NOT_ the same as pressed on current frame. They key may of have been
|
||||
held down over multiple frames, so endedDown will remain true.
|
||||
|
||||
Pressed on current frame captures if it was just pressed on this frame
|
||||
only.
|
||||
*/
|
||||
enum KeyStateFlag
|
||||
{
|
||||
keystateflag_ended_down = (1 << 0),
|
||||
keystateflag_pressed_on_curr_frame = (1 << 1),
|
||||
};
|
||||
|
||||
typedef struct KeyState
|
||||
{
|
||||
f32 delayInterval;
|
||||
u32 oldHalfTransitionCount;
|
||||
u32 newHalfTransitionCount;
|
||||
u32 flags;
|
||||
b32 endedDown;
|
||||
} KeyState;
|
||||
|
||||
typedef struct InputBuffer
|
||||
typedef struct KeyInput
|
||||
{
|
||||
OptimalArrayV2 *resolutionList;
|
||||
|
||||
v2 mouseP;
|
||||
KeyState keys[keycode_count];
|
||||
} InputBuffer;
|
||||
} KeyInput;
|
||||
|
||||
typedef struct PlatformFileRead
|
||||
{
|
||||
@ -156,37 +140,17 @@ typedef struct PlatformFileRead
|
||||
} PlatformFileRead;
|
||||
|
||||
// TODO(doyle): Create own custom memory allocator
|
||||
#define PLATFORM_MEM_FREE_(arena, ptr, bytes) \
|
||||
platform_memoryFree(arena, CAST(void *) ptr, bytes)
|
||||
#define PLATFORM_MEM_FREE_(arena, ptr, bytes) platform_memoryFree(arena, CAST(void *) ptr, bytes)
|
||||
// TODO(doyle): numBytes in mem free is temporary until we create custom
|
||||
// allocator since we haven't put in a system to track memory usage per
|
||||
// allocation
|
||||
void platform_memoryFree(MemoryArena_ *arena, void *data, MemoryIndex numBytes);
|
||||
void platform_memoryFree(MemoryArena_ *arena, void *data, i32 numBytes);
|
||||
|
||||
#define PLATFORM_MEM_ALLOC_(arena, num, type) \
|
||||
CAST(type *) platform_memoryAlloc(arena, num * sizeof(type))
|
||||
void *platform_memoryAlloc(MemoryArena_ *arena, MemoryIndex numBytes);
|
||||
#define PLATFORM_MEM_ALLOC_(arena, num, type) CAST(type *) platform_memoryAlloc(arena, num * sizeof(type))
|
||||
void *platform_memoryAlloc(MemoryArena_ *arena, i32 numBytes);
|
||||
|
||||
void platform_closeFileRead(MemoryArena_ *arena, PlatformFileRead *file);
|
||||
i32 platform_readFileToBuffer(MemoryArena_ *arena, const char *const filePath,
|
||||
PlatformFileRead *file);
|
||||
|
||||
/*
|
||||
NOTE(doyle): The keyinput functions are technically not for "communicating to
|
||||
the platform layer", but I've decided to group it here alongside the input
|
||||
data definitions. Since we require that the platform layer writes directly
|
||||
into our input buffer located in the game state.
|
||||
*/
|
||||
#define KEY_DELAY_NONE 0.0f
|
||||
|
||||
enum ReadKeyType
|
||||
{
|
||||
readkeytype_one_shot,
|
||||
readkeytype_delay_repeat,
|
||||
readkeytype_repeat,
|
||||
readkeytype_count,
|
||||
};
|
||||
void platform_inputBufferProcess(InputBuffer *inputBuffer, f32 dt);
|
||||
b32 platform_queryKey(KeyState *key, enum ReadKeyType readType,
|
||||
f32 delayInterval);
|
||||
#endif
|
||||
|
@ -3,20 +3,20 @@
|
||||
|
||||
#include "Dengine/Common.h"
|
||||
#include "Dengine/Math.h"
|
||||
#include "Dengine/AssetManager.h"
|
||||
|
||||
/* Forward Declaration */
|
||||
typedef struct Font Font;
|
||||
typedef struct AssetManager AssetManager;
|
||||
typedef struct Entity Entity;
|
||||
typedef struct Font Font;
|
||||
typedef struct MemoryArena MemoryArena_;
|
||||
typedef struct Shader Shader;
|
||||
typedef struct Texture Texture;
|
||||
|
||||
typedef struct RenderVertex
|
||||
typedef struct Vertex
|
||||
{
|
||||
v2 pos;
|
||||
v2 texCoord;
|
||||
} RenderVertex;
|
||||
} Vertex;
|
||||
|
||||
typedef struct RenderTex
|
||||
{
|
||||
@ -24,177 +24,66 @@ typedef struct RenderTex
|
||||
v4 texRect;
|
||||
} RenderTex;
|
||||
|
||||
typedef u32 RenderFlags;
|
||||
enum RenderFlag {
|
||||
renderflag_wireframe = 0x1,
|
||||
renderflag_no_texture = 0x2,
|
||||
};
|
||||
|
||||
// TODO(doyle): Since all vertexes are built with degenerate vertices and
|
||||
// in a triangle strip format, we should not split render groups by render mode
|
||||
// nor either generate buffers vao/vbo based on rendermode count
|
||||
enum RenderMode
|
||||
typedef struct RenderQuad
|
||||
{
|
||||
rendermode_quad,
|
||||
rendermode_polygon,
|
||||
rendermode_count,
|
||||
rendermode_invalid,
|
||||
};
|
||||
Vertex vertex[4];
|
||||
} RenderQuad_;
|
||||
|
||||
typedef struct RenderGroup
|
||||
{
|
||||
b32 init;
|
||||
RenderFlags flags;
|
||||
enum RenderMode mode;
|
||||
i32 zDepth;
|
||||
|
||||
// NOTE(doyle): Only for when adding singular triangles in triangle strip
|
||||
// mode
|
||||
b32 clockwiseWinding;
|
||||
|
||||
Texture *tex;
|
||||
v4 color;
|
||||
|
||||
RenderVertex *vertexList;
|
||||
Vertex *vertexList;
|
||||
i32 vertexIndex;
|
||||
|
||||
} RenderGroup;
|
||||
|
||||
enum VertexBatchState {
|
||||
vertexbatchstate_off,
|
||||
vertexbatchstate_initial_add,
|
||||
vertexbatchstate_active,
|
||||
};
|
||||
|
||||
typedef struct Renderer
|
||||
{
|
||||
// rendererf
|
||||
u32 shaderList[shaderlist_count];
|
||||
u32 activeShaderId;
|
||||
|
||||
u32 vao[rendermode_count];
|
||||
u32 vbo[rendermode_count];
|
||||
|
||||
enum VertexBatchState vertexBatchState;
|
||||
i32 groupIndexForVertexBatch;
|
||||
|
||||
Shader *shader;
|
||||
u32 vao;
|
||||
u32 vbo;
|
||||
i32 numVertexesInVbo;
|
||||
v2 vertexNdcFactor;
|
||||
v2 size;
|
||||
|
||||
// NOTE(doyle): Reference scale is the size chosen to have the best
|
||||
// playability based on the sizes of the entity given to the system. Any
|
||||
// other resoluions will be scaled through the renderer so that objects
|
||||
// remain the same size in different resolutions.
|
||||
f32 displayScale;
|
||||
v2 referenceScale;
|
||||
|
||||
RenderGroup groups[128];
|
||||
i32 groupsInUse;
|
||||
i32 groupCapacity;
|
||||
} Renderer;
|
||||
|
||||
void renderer_updateSize(Renderer *renderer, AssetManager *assetManager, v2 windowSize);
|
||||
void renderer_init(Renderer *renderer, AssetManager *assetManager,
|
||||
MemoryArena_ *persistentArena, v2 windowSize);
|
||||
|
||||
// TODO(doyle): Use z-index occluding for rendering
|
||||
RenderTex renderer_createNullRenderTex(AssetManager *const assetManager);
|
||||
|
||||
// TODO(doyle): Rectangles with gradient alphas/gradient colours
|
||||
// TODO(doyle): Clean up lines
|
||||
// Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); }
|
||||
void renderer_rect(Renderer *const renderer, Rect camera, v2 pos, v2 size,
|
||||
v2 pivotPoint, Radians rotate, RenderTex *renderTex,
|
||||
v4 color, i32 zDepth, RenderFlags flags);
|
||||
v2 pivotPoint, f32 rotate, RenderTex renderTex, v4 color);
|
||||
|
||||
inline void renderer_rectOutline(Renderer *const renderer, Rect camera, v2 pos,
|
||||
v2 size, f32 thickness, v2 pivotPoint,
|
||||
Radians rotate, RenderTex *renderTex, v4 color,
|
||||
i32 zDepth, RenderFlags flags)
|
||||
{
|
||||
// TODO(doyle): Pivot point is probably not correct!
|
||||
// TODO(doyle): Rotation doesn't work!
|
||||
ASSERT(rotate == 0);
|
||||
|
||||
/* Bottom line */
|
||||
renderer_rect(renderer, camera, pos, V2(size.w, thickness), pivotPoint,
|
||||
rotate, renderTex, color, zDepth, flags);
|
||||
|
||||
/* Top line */
|
||||
v2 topP = v2_add(pos, V2(0, size.h - thickness));
|
||||
renderer_rect(renderer, camera, topP, V2(size.w, thickness), pivotPoint,
|
||||
rotate, renderTex, color, zDepth, flags);
|
||||
|
||||
/* Left line */
|
||||
renderer_rect(renderer, camera, pos, V2(thickness, size.h), pivotPoint,
|
||||
rotate, renderTex, color, zDepth, flags);
|
||||
|
||||
/* Right line */
|
||||
v2 rightP = v2_add(pos, V2(size.w - thickness, 0));
|
||||
renderer_rect(renderer, camera, rightP, V2(thickness, size.h), pivotPoint,
|
||||
rotate, renderTex, color, zDepth, flags);
|
||||
}
|
||||
|
||||
inline void renderer_rectFixed(Renderer *const renderer, v2 pos, v2 size,
|
||||
v2 pivotPoint, Radians rotate,
|
||||
RenderTex *renderTex, v4 color, i32 zDepth,
|
||||
RenderFlags flags)
|
||||
inline void renderer_staticRect(Renderer *const renderer, v2 pos, v2 size,
|
||||
v2 pivotPoint, f32 rotate, RenderTex renderTex,
|
||||
v4 color)
|
||||
{
|
||||
Rect staticCamera = {V2(0, 0), renderer->size};
|
||||
renderer_rect(renderer, staticCamera, pos, size, pivotPoint, rotate,
|
||||
renderTex, color, zDepth, flags);
|
||||
renderTex, color);
|
||||
}
|
||||
|
||||
inline void renderer_rectFixedOutline(Renderer *const renderer, v2 pos, v2 size,
|
||||
v2 pivotPoint, f32 thickness,
|
||||
Radians rotate, RenderTex *renderTex,
|
||||
v4 color, i32 zDepth, RenderFlags flags)
|
||||
{
|
||||
Rect staticCamera = {V2(0, 0), renderer->size};
|
||||
renderer_rectOutline(renderer, staticCamera, pos, size, thickness,
|
||||
pivotPoint, rotate, renderTex, color, zDepth, flags);
|
||||
}
|
||||
void renderer_string(Renderer *const renderer, MemoryArena_ *arena,
|
||||
Rect camera, Font *const font,
|
||||
const char *const string, v2 pos, v2 pivotPoint,
|
||||
f32 rotate, v4 color);
|
||||
|
||||
|
||||
void renderer_polygon(Renderer *const renderer, Rect camera, v2 *polygonPoints,
|
||||
i32 numPoints, v2 pivotPoint, Radians rotate,
|
||||
RenderTex *renderTex, v4 color, i32 zDepth,
|
||||
RenderFlags flags);
|
||||
|
||||
|
||||
void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera,
|
||||
Font *const font, const char *const string, v2 pos,
|
||||
v2 pivotPoint, Radians rotate, v4 color, i32 zDepth,
|
||||
RenderFlags flags);
|
||||
|
||||
inline void renderer_stringFixed(Renderer *const renderer, MemoryArena_ *arena,
|
||||
inline void renderer_staticString(Renderer *const renderer, MemoryArena_ *arena,
|
||||
Font *const font, const char *const string,
|
||||
v2 pos, v2 pivotPoint, Radians rotate,
|
||||
v4 color, i32 zDepth, RenderFlags flags)
|
||||
v2 pos, v2 pivotPoint, f32 rotate, v4 color)
|
||||
{
|
||||
Rect staticCamera = {V2(0, 0), renderer->size};
|
||||
renderer_string(renderer, arena, staticCamera, font, string, pos,
|
||||
pivotPoint, rotate, color, zDepth, flags);
|
||||
pivotPoint, rotate, color);
|
||||
}
|
||||
|
||||
inline void renderer_stringFixedCentered(Renderer *const renderer,
|
||||
MemoryArena_ *arena, Font *const font,
|
||||
const char *const string, v2 pos,
|
||||
v2 pivotPoint, Radians rotate,
|
||||
v4 color, i32 zDepth,
|
||||
enum RenderFlags flags)
|
||||
{
|
||||
Rect staticCamera = {V2(0, 0), renderer->size};
|
||||
|
||||
v2 dim = asset_fontStringDimInPixels(font, string);
|
||||
v2 halfDim = v2_scale(dim, 0.5f);
|
||||
pos = v2_sub(pos, halfDim);
|
||||
|
||||
renderer_string(renderer, arena, staticCamera, font, string, pos,
|
||||
pivotPoint, rotate, color, zDepth, flags);
|
||||
}
|
||||
|
||||
void renderer_entity(Renderer *renderer, MemoryArena_ *transientArena,
|
||||
Rect camera, Entity *entity, v2 pivotPoint, Degrees rotate,
|
||||
v4 color, i32 zDepth, RenderFlags flags);
|
||||
void renderer_entity(Renderer *renderer, Rect camera, Entity *entity,
|
||||
v2 pivotPoint, f32 rotate, v4 color);
|
||||
|
||||
void renderer_renderGroups(Renderer *renderer);
|
||||
|
||||
|
21
src/include/Dengine/Shader.h
Normal file
21
src/include/Dengine/Shader.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef DENGINE_SHADER_H
|
||||
#define DENGINE_SHADER_H
|
||||
|
||||
#include "Dengine/Math.h"
|
||||
#include "Dengine/OpenGL.h"
|
||||
|
||||
typedef struct Shader
|
||||
{
|
||||
GLuint id;
|
||||
} Shader;
|
||||
|
||||
void shader_uniformSet1i(Shader *const shader, const GLchar *name,
|
||||
const GLuint data);
|
||||
void shader_uniformSetMat4fv(Shader *const shader, const GLchar *name,
|
||||
mat4 data);
|
||||
void shader_uniformSetVec4f(Shader *const shader, const GLchar *name,
|
||||
v4 data);
|
||||
|
||||
void shader_use(const Shader *const shader);
|
||||
|
||||
#endif
|
16
src/include/Dengine/String.h
Normal file
16
src/include/Dengine/String.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef DENGINE_STRING_H
|
||||
#define DENGINE_STRING_H
|
||||
|
||||
#include "Dengine/Common.h"
|
||||
|
||||
typedef struct MemoryArena MemoryArena_;
|
||||
typedef char String;
|
||||
|
||||
i32 string_len(String *const string);
|
||||
String *const string_append(MemoryArena_ *const arena, String *oldString,
|
||||
String *appendString, i32 appendLen);
|
||||
void string_free(MemoryArena_ *arena, String *string);
|
||||
String *const string_make(MemoryArena_ *const arena, char *string);
|
||||
String *const string_makeLen(MemoryArena_ *const arena, i32 len);
|
||||
|
||||
#endif
|
39
src/include/Dengine/Texture.h
Normal file
39
src/include/Dengine/Texture.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef DENGINE_TEXTURE_H
|
||||
#define DENGINE_TEXTURE_H
|
||||
|
||||
#include "Dengine/Common.h"
|
||||
#include "Dengine/OpenGL.h"
|
||||
|
||||
#define TARGET_TEXTURE_SIZE 1024
|
||||
#define TARGET_BYTES_PER_PIXEL 4
|
||||
|
||||
// TODO(doyle): Look into merging into assets.h file ..
|
||||
typedef struct Texture
|
||||
{
|
||||
// Holds the ID of the texture object, used for all texture operations to
|
||||
// reference to this particlar texture
|
||||
GLuint id;
|
||||
|
||||
// Texture image dimensions
|
||||
GLuint width;
|
||||
GLuint height;
|
||||
|
||||
// Texture Format
|
||||
GLuint internalFormat; // Format of texture object
|
||||
GLuint imageFormat; // Format of loaded image
|
||||
|
||||
// Texture configuration
|
||||
GLuint wrapS; // Wrapping mode on S axis
|
||||
GLuint wrapT; // Wrapping mode on T axis
|
||||
|
||||
// Filtering mode if texture pixels < screen pixels
|
||||
GLuint filterMinification;
|
||||
// Filtering mode if texture pixels > screen pixels
|
||||
GLuint filterMagnification;
|
||||
} Texture;
|
||||
|
||||
// Generates texture from image data
|
||||
Texture texture_gen(const GLuint width, const GLuint height,
|
||||
const GLint bytesPerPixel, const u8 *const image);
|
||||
|
||||
#endif
|
@ -1,78 +0,0 @@
|
||||
#ifndef DENGINE_USER_INTERFACE_H
|
||||
#define DENGINE_USER_INTERFACE_H
|
||||
|
||||
#include "Dengine/Common.h"
|
||||
#include "Dengine/Math.h"
|
||||
#include "Dengine/Platform.h"
|
||||
|
||||
/* Forward Declaration */
|
||||
typedef struct AssetManager AssetManager;
|
||||
typedef struct Font Font;
|
||||
typedef struct MemoryArena MemoryArena_;
|
||||
typedef struct Renderer Renderer;
|
||||
typedef struct GameState GameState;
|
||||
|
||||
enum UiType
|
||||
{
|
||||
uitype_button,
|
||||
uitype_scrollbar,
|
||||
uitype_textField,
|
||||
uitype_string,
|
||||
uitype_count,
|
||||
};
|
||||
|
||||
typedef struct UiItem
|
||||
{
|
||||
i32 id;
|
||||
char label[64];
|
||||
enum UiType type;
|
||||
|
||||
Rect rect;
|
||||
|
||||
// TODO(doyle): ECS this? Not all elements need
|
||||
i32 value;
|
||||
i32 maxValue;
|
||||
char string[80];
|
||||
} UiItem;
|
||||
|
||||
typedef struct UiState
|
||||
{
|
||||
i32 uniqueId;
|
||||
|
||||
UiItem uiList[128];
|
||||
i32 numItems;
|
||||
|
||||
i32 hotItem;
|
||||
i32 activeItem;
|
||||
i32 lastWidget;
|
||||
|
||||
i32 kbdItem;
|
||||
enum KeyCode keyEntered;
|
||||
enum KeyCode keyMod;
|
||||
enum KeyCode keyChar;
|
||||
} UiState;
|
||||
|
||||
inline i32 ui_generateId(UiState *const uiState)
|
||||
{
|
||||
i32 result = uiState->uniqueId++;
|
||||
return result;
|
||||
}
|
||||
|
||||
void ui_beginState(UiState *state);
|
||||
void ui_endState(UiState *state, InputBuffer *input);
|
||||
|
||||
i32 ui_button(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager, Renderer *const renderer,
|
||||
Font *const font, const InputBuffer input, const i32 id,
|
||||
const Rect rect, const char *const label, i32 zDepth);
|
||||
|
||||
i32 ui_textfield(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager, Renderer *const renderer,
|
||||
Font *const font, InputBuffer input, const i32 id,
|
||||
const Rect rect, char *const string, i32 zDepth);
|
||||
|
||||
i32 ui_scrollbar(UiState *const uiState, AssetManager *const assetManager,
|
||||
Renderer *const renderer, const InputBuffer input,
|
||||
const i32 id, const Rect scrollBarRect, i32 *const value,
|
||||
const i32 maxValue, i32 zDepth);
|
||||
#endif
|
101
src/include/Dengine/UserInterface.h
Normal file
101
src/include/Dengine/UserInterface.h
Normal file
@ -0,0 +1,101 @@
|
||||
#ifndef DENGINE_USER_INTERFACE_H
|
||||
#define DENGINE_USER_INTERFACE_H
|
||||
|
||||
#include "Dengine/Common.h"
|
||||
#include "Dengine/Math.h"
|
||||
#include "Dengine/Platform.h"
|
||||
|
||||
/* Forward Declaration */
|
||||
typedef struct AssetManager AssetManager;
|
||||
typedef struct Font Font;
|
||||
typedef struct MemoryArena MemoryArena_;
|
||||
typedef struct Renderer Renderer;
|
||||
|
||||
enum UiType
|
||||
{
|
||||
uitype_button,
|
||||
uitype_scrollbar,
|
||||
uitype_textField,
|
||||
uitype_string,
|
||||
uitype_count,
|
||||
};
|
||||
|
||||
typedef struct UiItem
|
||||
{
|
||||
i32 id;
|
||||
char label[64];
|
||||
enum UiType type;
|
||||
|
||||
Rect rect;
|
||||
|
||||
// TODO(doyle): ECS this? Not all elements need
|
||||
i32 value;
|
||||
i32 maxValue;
|
||||
char string[80];
|
||||
} UiItem;
|
||||
|
||||
typedef struct WindowState
|
||||
{
|
||||
char title[64];
|
||||
i32 id;
|
||||
|
||||
UiItem childUiItems[16];
|
||||
i32 numChildUiItems;
|
||||
|
||||
Rect rect;
|
||||
// TODO(doyle): Store this in the input data not window?
|
||||
v2 prevMouseP;
|
||||
|
||||
b32 prevFrameWindowHeld;
|
||||
b32 windowHeld;
|
||||
} WindowState;
|
||||
|
||||
typedef struct UiState
|
||||
{
|
||||
i32 uniqueId;
|
||||
|
||||
UiItem uiList[128];
|
||||
i32 numItems;
|
||||
|
||||
i32 hotItem;
|
||||
i32 activeItem;
|
||||
i32 lastWidget;
|
||||
|
||||
i32 kbdItem;
|
||||
enum KeyCode keyEntered;
|
||||
enum KeyCode keyMod;
|
||||
enum KeyCode keyChar;
|
||||
|
||||
WindowState statWindow;
|
||||
WindowState debugWindow;
|
||||
} UiState;
|
||||
|
||||
inline i32 userInterface_generateId(UiState *const uiState)
|
||||
{
|
||||
i32 result = uiState->uniqueId++;
|
||||
return result;
|
||||
}
|
||||
|
||||
i32 userInterface_button(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer, Font *const font,
|
||||
const KeyInput input, const i32 id, const Rect rect,
|
||||
const char *const label);
|
||||
|
||||
i32 userInterface_textField(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer, Font *const font,
|
||||
KeyInput input, const i32 id, const Rect rect,
|
||||
char *const string);
|
||||
|
||||
i32 userInterface_scrollbar(UiState *const uiState,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer, const KeyInput input,
|
||||
const i32 id, const Rect scrollBarRect,
|
||||
i32 *const value, const i32 maxValue);
|
||||
|
||||
i32 userInterface_window(UiState *const uiState, MemoryArena_ *const arena,
|
||||
AssetManager *const assetManager,
|
||||
Renderer *const renderer, Font *const font,
|
||||
const KeyInput input, WindowState *window);
|
||||
#endif
|
109
src/include/Dengine/WorldTraveller.h
Normal file
109
src/include/Dengine/WorldTraveller.h
Normal file
@ -0,0 +1,109 @@
|
||||
#ifndef WORLDTRAVELLER_GAME_H
|
||||
#define WORLDTRAVELLER_GAME_H
|
||||
|
||||
#include "Dengine/AssetManager.h"
|
||||
#include "Dengine/Audio.h"
|
||||
#include "Dengine/Common.h"
|
||||
#include "Dengine/Math.h"
|
||||
#include "Dengine/MemoryArena.h"
|
||||
#include "Dengine/Platform.h"
|
||||
#include "Dengine/Renderer.h"
|
||||
#include "Dengine/UserInterface.h"
|
||||
|
||||
#define NUM_KEYS 1024
|
||||
#define METERS_TO_PIXEL 240
|
||||
|
||||
/* Forward declaration */
|
||||
typedef struct Entity Entity;
|
||||
|
||||
enum EventType
|
||||
{
|
||||
eventtype_null = 0,
|
||||
eventtype_start_attack,
|
||||
eventtype_end_attack,
|
||||
eventtype_start_anim,
|
||||
eventtype_end_anim,
|
||||
eventtype_entity_died,
|
||||
eventtype_count,
|
||||
};
|
||||
|
||||
typedef struct Event
|
||||
{
|
||||
enum EventType type;
|
||||
void *data;
|
||||
} Event;
|
||||
|
||||
typedef struct EventQueue
|
||||
{
|
||||
Event queue[1024];
|
||||
i32 numEvents;
|
||||
} EventQueue;
|
||||
|
||||
enum RectBaseline
|
||||
{
|
||||
rectbaseline_top,
|
||||
rectbaseline_topLeft,
|
||||
rectbaseline_topRight,
|
||||
rectbaseline_bottom,
|
||||
rectbaseline_bottomRight,
|
||||
rectbaseline_bottomLeft,
|
||||
rectbaseline_left,
|
||||
rectbaseline_right,
|
||||
rectbaseline_center,
|
||||
rectbaseline_count,
|
||||
|
||||
};
|
||||
|
||||
typedef struct Config
|
||||
{
|
||||
b32 playWorldAudio;
|
||||
b32 showStatMenu;
|
||||
b32 showDebugDisplay;
|
||||
} Config;
|
||||
|
||||
typedef struct World
|
||||
{
|
||||
Entity *entities;
|
||||
i32 maxEntities;
|
||||
b32 *entityIdInBattle;
|
||||
i32 numEntitiesInBattle;
|
||||
|
||||
i32 cameraFollowingId;
|
||||
v2 cameraPos; // In pixels
|
||||
v4 bounds; // In pixels
|
||||
|
||||
i32 heroId;
|
||||
i32 freeEntityIndex;
|
||||
u32 uniqueIdAccumulator;
|
||||
|
||||
Entity *soundscape;
|
||||
} World;
|
||||
|
||||
typedef struct GameState
|
||||
{
|
||||
Memory *memory;
|
||||
MemoryArena_ arena_;
|
||||
MemoryArena_ transientArena;
|
||||
|
||||
enum State state;
|
||||
KeyInput input;
|
||||
v2 mouse;
|
||||
|
||||
Renderer renderer;
|
||||
|
||||
World world[4];
|
||||
i32 currWorldIndex;
|
||||
i32 tileSize;
|
||||
|
||||
AssetManager assetManager;
|
||||
AudioManager audioManager;
|
||||
Config config;
|
||||
UiState uiState;
|
||||
EventQueue eventQueue;
|
||||
} GameState;
|
||||
|
||||
void worldTraveller_gameInit(GameState *state, v2 windowSize, Memory *memory);
|
||||
void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt);
|
||||
void worldTraveller_registerEvent(EventQueue *eventQueue, enum EventType type,
|
||||
void *data);
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user