Compare commits

..

88 Commits

Author SHA1 Message Date
Till Wübbers
d6e0cbf41c more tutorial text 2025-06-26 11:12:10 +02:00
Asuro
f95d9dee91 final build ig 2025-06-21 12:31:46 +02:00
Asuro
3c0af71470 puzzle 2025-06-21 12:21:14 +02:00
Asuro
7e89d93a7d bridge 2025-06-21 12:10:53 +02:00
Asuro
05cf88d986 fixes 2025-06-21 11:06:05 +02:00
Asuro
d6bec9e870 rotation fix 2025-06-21 10:57:36 +02:00
Asuro
3ccbbf493f walls 2025-06-21 03:27:25 +02:00
Asuro
b47a0cf841 test puzzles 2025-06-21 00:26:44 +02:00
Asuro
db47297ea4 less sticky movement 2025-06-21 00:26:37 +02:00
Asuro
6461b442de (kinda) fix ui offset 2025-06-21 00:13:51 +02:00
Asuro
146bf4aa22 color changes 2025-06-21 00:04:33 +02:00
Asuro
42c5b55f95 fix texture rotation, add tutorial popup 2025-06-20 23:34:32 +02:00
Asuro
d7fc6b781e ui hack 2025-06-20 15:42:05 +02:00
Asuro
e15cd79e04 fix collision 2025-06-20 15:41:52 +02:00
Asuro
4e00355dbe working collision! 2025-06-20 05:15:35 +02:00
Asuro
ffcc5bd134 fix heightmaps 2025-06-20 03:00:42 +02:00
Till Wübbers
67c1489da0 heightmap wip 2025-06-18 00:28:40 +02:00
Till Wübbers
59b8eea3a7 heightmap previews 2025-06-13 13:10:31 +02:00
Till Wübbers
a936222711 new debug message 2025-06-01 14:26:13 +02:00
Asuro
3af10d120b landscape 2025-06-01 03:17:29 +02:00
Asuro
6c8bead6ab new dithering 2025-06-01 03:17:21 +02:00
Asuro
faa36dd679 fix dithergen 2025-06-01 02:06:32 +02:00
Asuro
196d119338 slight entity rework (todo: memory corruption >.<) 2025-05-31 01:39:34 +02:00
Asuro
383c6f975b fix asset upgrade for arrays 2025-05-31 00:20:23 +02:00
Till Wübbers
4b230be2a8 half assed bugfixing 2025-05-29 17:33:14 +02:00
Till W
87ce032833 wip 2025-05-26 18:04:51 +02:00
Till W
cac2ef3330 new puzzle tool 2025-05-25 13:19:50 +02:00
Till W
410f401aef flags enum stuff 2025-05-25 13:07:11 +02:00
Till W
0c2d10d631 fix upgrading native type changes 2025-05-25 10:34:28 +02:00
Till W
18e7085aeb build notes 2025-05-23 01:57:32 +02:00
Asuro
bd2962fc38 refactor 2025-05-21 02:48:36 +02:00
Asuro
15dc65530d reset 2025-05-19 18:41:10 +02:00
Asuro
70db6ca2aa solved status ui 2025-05-19 18:20:05 +02:00
Asuro
1a8be39c19 border shader 2025-05-19 18:19:43 +02:00
Asuro
ec7a709570 solved not solved textures 2025-05-19 18:19:21 +02:00
Asuro
5cdc7d720e socket connection direction wip 2025-05-16 01:50:19 +02:00
Asuro
79fe91981b small fixes 2025-05-16 00:26:19 +02:00
Asuro
0d91ec1ebb working sockets 2025-05-11 22:32:50 +02:00
Asuro
1616704c50 fix upgrades one more time 2025-05-10 19:09:59 +02:00
Asuro
5d1db591b7 fix log highlighting 2025-05-10 14:44:25 +02:00
Till Wübbers
234a9b1732 correctly destroy stuff 2025-04-29 09:52:19 +02:00
Till Wübbers
91e9566747 make imgui optional-ish 2025-04-29 08:46:05 +02:00
Till Wübbers
02c40aeea6 more fixes 2025-04-29 08:01:26 +02:00
Till Wübbers
171e25ac76 fix memory arena alloc 2025-04-28 10:03:09 +02:00
Till Wübbers
32d89d8f77 logging improvements 2025-04-28 08:23:16 +02:00
Till Wübbers
4cdd80977c wip 2025-04-27 12:00:22 +02:00
Asuro
d75e5627f9 stuff 2025-04-13 01:46:36 +02:00
Asuro
adbe518c6e fix enum upgrades 2025-04-13 00:24:03 +02:00
Till Wübbers
c9db7e7e8f smol fix 2025-04-07 19:26:35 +02:00
Asuro
d252da6359 puzzles idk 2025-04-07 18:04:59 +02:00
Asuro
3fd8937b25 drag available cards 2025-04-07 17:58:48 +02:00
Asuro
fd2654c944 multiple puzzles 2025-04-07 16:57:41 +02:00
Asuro
d0f9051af7 puzzle data 2025-04-07 16:57:28 +02:00
Asuro
e0016817dd card assets 2025-04-07 01:32:07 +02:00
Asuro
c7edebaeb8 upgradeable types! 2025-04-06 22:27:01 +02:00
Asuro
158d59e09e new save format 2025-04-05 18:10:43 +02:00
Asuro
ab5c8a489f generate metadata in code 2025-04-04 20:39:31 +02:00
Asuro
12c546b6dc save ui settings & toggle ui 2025-03-31 18:12:23 +02:00
Asuro
ae069c4949 puzzle ui positioning 2025-03-31 17:51:57 +02:00
Asuro
5a3739db0a show game tablet 2025-03-31 07:08:23 +02:00
Asuro
4ba65713ef move transform to generated data 2025-03-31 02:01:14 +02:00
Asuro
29a3aaf241 puzzle ui 2025-03-30 20:47:09 +02:00
Asuro
c244b997c1 moving more stuff around 2025-03-30 20:22:16 +02:00
Asuro
6d170be57a include cleanup 2025-03-30 20:17:32 +02:00
Asuro
052fc2cc07 move vec to generated 2025-03-30 19:45:48 +02:00
Asuro
b006d14197 card dragging 2025-03-30 03:00:23 +02:00
Asuro
e5076b0c3b it works! 2025-03-30 00:05:52 +01:00
Asuro
3ce1acc633 wip card clicking 2025-03-29 22:27:34 +01:00
Asuro
cd03f89465 flip quad uvs 2025-03-29 17:49:10 +01:00
Asuro
39164bffa5 move imgui helper files 2025-03-29 17:27:17 +01:00
Asuro
ef7833caa6 fix camera matrix hacks 2025-03-29 17:10:26 +01:00
Asuro
dbc65fec9a toggle stats 2025-03-29 16:20:45 +01:00
Till Wübbers
cdf6a83af6 cleanup 2025-03-29 06:22:14 +01:00
Till Wübbers
77a6a0a510 fps display 2025-03-29 05:37:07 +01:00
Till Wübbers
fc67ff62be cleanup 2025-03-29 05:10:13 +01:00
Asuro
365805537c texture loading! 2025-03-28 04:28:06 +01:00
Asuro
f56ffdaa13 shader changes & screen quads 2025-03-28 00:11:56 +01:00
Asuro
26ba03b7fe parent to camera 2025-03-27 22:38:31 +01:00
Asuro
90d4c3df1b more logging stuff 2025-03-27 22:17:58 +01:00
Asuro
2ff08d7258 show log in game 2025-03-27 17:36:55 +01:00
Asuro
24a724a021 fix mesh indexing 2025-03-27 16:52:34 +01:00
Asuro
aafa5e966e fixing stuff 2025-03-24 18:30:59 +01:00
Asuro
fa93abe1ec serialization stuff 2025-03-24 16:35:49 +01:00
Asuro
551796e7ad generate type data 2025-03-20 20:18:40 +01:00
Asuro
e16c9e0a2f fixed puzzle solver! 2025-03-17 18:33:09 +01:00
Asuro
ffd2e45d81 working puzzle editor? 2025-03-17 17:59:39 +01:00
Till Wübbers
93bb1553be wip 2025-03-16 21:03:41 +01:00
Till Wübbers
ebf29b058a release build setup 2025-03-16 02:00:33 +01:00
145 changed files with 7957 additions and 1616 deletions

2
.gitattributes vendored
View File

@@ -6,3 +6,5 @@
*.blend filter=lfs diff=lfs merge=lfs -text
*.pzl filter=lfs diff=lfs merge=lfs -text
*.exe filter=lfs diff=lfs merge=lfs -text
*.glb filter=lfs diff=lfs merge=lfs -text
*.ktx filter=lfs diff=lfs merge=lfs -text

Binary file not shown.

BIN
assets/blender/cards.blend LFS Normal file

Binary file not shown.

BIN
assets/blender/landscape.blend LFS Normal file

Binary file not shown.

BIN
assets/blender/tablet.blend LFS Normal file

Binary file not shown.

BIN
assets/textures/bridge.png LFS Normal file

Binary file not shown.

BIN
assets/textures/concrete_tile.png LFS Normal file

Binary file not shown.

BIN
assets/textures/notsolved.png LFS Normal file

Binary file not shown.

BIN
assets/textures/reset.png LFS Normal file

Binary file not shown.

BIN
assets/textures/solved.png LFS Normal file

Binary file not shown.

BIN
assets/textures/test.png LFS Normal file

Binary file not shown.

BIN
assets/textures/w corner long.png LFS Normal file

Binary file not shown.

BIN
assets/textures/w straight.png LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
src/.gitattributes vendored
View File

@@ -1 +0,0 @@
*.glb filter=lfs diff=lfs merge=lfs -text

1
src/.gitignore vendored
View File

@@ -33,5 +33,6 @@
# build dirs
cmake-build/
cmake-build-release/
out/
.vs/

View File

@@ -2,17 +2,18 @@ cmake_minimum_required(VERSION 3.10)
project(PuzGameProj)
if (MSVC)
else()
add_compile_options(-g -gcodeview)
add_link_options(-fuse-ld=lld -g -Wl,--pdb=)
endif()
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_compile_definitions("$<$<CONFIG:DEBUG>:DEBUG>")
# set the output directory for built objects.
# This makes sure that the dynamic library goes into the build directory automatically.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIGURATION>")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIGURATION>")
set(PROFILE ON)
# Imgui
file(GLOB imgui_sources dependency/imgui/*.h dependency/imgui/*.cpp)
@@ -27,7 +28,6 @@ file(GLOB_RECURSE sources_engine engine/*.cpp engine/*.h)
add_executable(PuzGameEngine ${sources_engine})
target_compile_definitions(PuzGameEngine PUBLIC "_AMD64_")
set_property(TARGET PuzGameEngine PROPERTY CXX_STANDARD 17)
target_include_directories(PuzGameEngine PUBLIC dependency/tracy/public/)
# Game
add_custom_command(
@@ -39,13 +39,16 @@ add_custom_command(
file(GLOB_RECURSE sources_game game/*.cpp game/*.h gen/Generated.cpp gen/Generated.h gen/Def.h gen/Def.cpp)
file(GLOB source_singleheader dependency/tinygltf/stb_image.h dependency/tinygltf/stb_image_write.h dependency/tinygltf/json.hpp dependency/tinygltf/tiny_gltf.h)
add_library(PuzGame SHARED ${sources_game} ${source_singleheader} ${imgui_sources} ${imgui_backend_sdl})
file(GLOB sources_imgui_helper dependency/imgui-bgfx/imgui-helper.h dependency/imgui-bgfx/imgui-helper.cpp)
add_library(PuzGame SHARED ${sources_game} ${source_singleheader} ${imgui_sources} ${imgui_backend_sdl} ${sources_imgui_helper})
set_property(TARGET PuzGame PROPERTY CXX_STANDARD 17)
target_include_directories(PuzGame PUBLIC dependency/imgui)
target_include_directories(PuzGame PUBLIC dependency/tracy/public)
target_include_directories(PuzGame PUBLIC dependency/imgui-bgfx)
# Profiling
if (PROFILE)
option(TRACY_ENABLE "" ON)
option(TRACY_ENABLE "" OFF)
if (TRACY_ENABLE)
option(TRACY_ON_DEMAND "" ON)
set(TRACY_DELAYED_INIT ON)
set(TRACY_MANUAL_LIFETIME ON)
@@ -61,6 +64,11 @@ SET(BGFX_BUILD_EXAMPLES OFF)
add_subdirectory("${CMAKE_SOURCE_DIR}/dependency/bgfx.cmake")
# Link
if (TRACY_ENABLE)
target_link_libraries(PuzGame bx bimg bgfx SDL3::SDL3 Tracy::TracyClient)
target_link_libraries(PuzGameEngine bx SDL3::SDL3 Tracy::TracyClient)
else()
target_link_libraries(PuzGame bx bimg bgfx SDL3::SDL3)
target_link_libraries(PuzGameEngine bx SDL3::SDL3)
endif()
set_target_properties(PuzGame PROPERTIES OUTPUT_NAME "PuzGame2")

10
src/assetcompile.ps1 Normal file
View File

@@ -0,0 +1,10 @@
$texturecPath = "..\tools\texturec.exe"
$textureAssetDir = "..\assets\textures"
$outputBaseDir = ".\textures"
Get-ChildItem -Path $textureAssetDir -File | ForEach-Object {
$outName = Join-Path -Path $outputBaseDir -ChildPath ($_.BaseName + ".ktx")
Write-Host $_.FullName $outName
& "$texturecPath" -f $_.FullName -o $outName -t RGBA8
}

View File

@@ -1 +0,0 @@
cmake --build cmake-build

1
src/build_release.ps1 Normal file
View File

@@ -0,0 +1 @@
cmake --build cmake-build-release --config Release

1
src/buildnotes.txt Normal file
View File

@@ -0,0 +1 @@
https://github.com/mstorsjo/llvm-mingw/releases

View File

@@ -1,4 +0,0 @@
cd dependency/bgfx.cmake
cmake -G "Ninja" -S . -B cmake-build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DBGFX_BUILD_TOOLS=OFF -DBGFX_BUILD_EXAMPLES=OFF
cd ..\..
cmake -G "Ninja" -S . -B cmake-build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

View File

@@ -1,4 +1,6 @@
cd dependency/bgfx.cmake
cmake -G "Ninja" -S . -B cmake-build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DBGFX_BUILD_TOOLS=OFF -DBGFX_BUILD_EXAMPLES=OFF
cmake -G "Ninja" -S . -B cmake-build-release -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DBGFX_BUILD_TOOLS=OFF -DBGFX_BUILD_EXAMPLES=OFF
cd ..\..
cmake -G "Ninja" -S . -B cmake-build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake -G "Ninja" -S . -B cmake-build-release -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

2
src/debug.ps1 Normal file
View File

@@ -0,0 +1,2 @@
.\build.ps1
& raddbg.exe --project:../tools/radsession.rad --auto_run -q

View File

@@ -1,4 +1,6 @@
SDL/*
# !SDL/src
# bgfx.cmake/*
bgfx.cmake/*
tinygltf/*
imgui/*
iconfontheaders/*
tracy/*

View File

@@ -1,18 +0,0 @@
#pragma once
#include <cstdint>
#define LOG(line, fmt, ...) Logging::Log(Logging::ELogType::Log, line, fmt, ##__VA_ARGS__)
#define LOG_WARN(line, fmt, ...) Logging::Log(Logging::ELogType::Warn, line, fmt, ##__VA_ARGS__)
#define LOG_ERROR(line, fmt, ...) Logging::Log(Logging::ELogType::Error, line, fmt, ##__VA_ARGS__)
struct Logging
{
enum class ELogType
{
Log,
Warn,
Error,
};
static void Log(ELogType logType, uint32_t line, const char* format, ...);
};

View File

@@ -9,27 +9,30 @@
#include <winnt.h>
#define WIN32_LEAN_AND_MEAN
#include "Windows.h"
#include "Windows.h" // IWYU pragma: keep
namespace WriteTemplates
{
constexpr char FileHeaderStart[] =
R"END(#pragma once
#include "Def.h"
#include <cstdint>
namespace Generated
namespace Gen
{
struct Serializer;
struct Deserializer;
)END";
constexpr char FileCppStart[] = R"END(#include "Generated.h"
constexpr char FileCppStart[] = R"END(#include "Def.h"
#include "Generated.h"
namespace Generated
namespace Gen
{
)END";
constexpr char StructHeader2[] =
R"END( struct %s
{
static constexpr uint32_t Hash = %u;
static constexpr uint16_t TypeIdx = %u;
)END";
constexpr char StructField4[] =
@@ -43,7 +46,7 @@ namespace Generated
constexpr char EnumHeader4[] =
R"END( struct %s
{
static constexpr uint32_t Hash = %u;
static constexpr uint16_t TypeIdx = %u;
static constexpr int32_t EntryCount = %u;
enum Enum : %s
{
@@ -52,6 +55,9 @@ namespace Generated
constexpr char EnumField1[] =
R"END( %s,
)END";
constexpr char EnumFieldNumbered2[] =
R"END( %s = %s,
)END";
constexpr char EnumNamesStart2[] =
R"END( };
@@ -68,6 +74,25 @@ namespace Generated
};
)END";
constexpr char EnumFlagOperators12[] =
R"END( inline %s::Enum operator| (const %s::Enum& a, const %s::Enum& b)
{
return a | b;
}
inline %s::Enum operator& (const %s::Enum& a, const %s::Enum& b)
{
return a & b;
}
inline %s::Enum operator|= (%s::Enum& a, const %s::Enum& b)
{
return a |= b;
}
inline %s::Enum operator&= (%s::Enum& a, const %s::Enum& b)
{
return a &= b;
}
)END";
constexpr char FileEnd[] =
R"END(}
)END";
@@ -82,6 +107,8 @@ namespace Generated
{
)END";
constexpr char SaveFuncBodyType3[] = R"END( isOk = Save(%sobj[i].%s, %u, serializer) && isOk;
)END";
constexpr char SaveFuncBodyNative[] = R"END( isOk = serializer.Write(&obj[i], sizeof(obj[i])) && isOk;
)END";
constexpr char SaveFuncBodyEnum1[] = R"END( auto val = (%s)obj[i];
isOk = Save(&val, 1, serializer) && isOk;
@@ -96,18 +123,148 @@ namespace Generated
)END";
constexpr char LoadFuncBodyStart1[] = R"END( bool Load(%s* obj, uint32_t count, Deserializer& serializer)
{
)END";
constexpr char LoadFuncBodySetupStart2[] =
R"END( const char* typeName = Meta::Metadata.TypeDefinitions[%s::TypeIdx].Name;
// Quick match
int32_t matchedHashIdx =
serializer.TypeBuf.FindHash(Meta::Metadata.TypeDefinitions[%s::TypeIdx].Hash);
if (matchedHashIdx >= 0)
{
assert(bx::strCmp(serializer.TypeBuf.Defs[matchedHashIdx].Name, typeName) == 0);
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
)END";
constexpr char LoadFuncBodyType3[] = R"END( isOk = Load(%sobj[i].%s, %u, serializer) && isOk;
constexpr char LoadFuncBodyQuickLoad2[] = R"END( isOk = Load(%sobj[i].%s, %u, serializer) && isOk;
)END";
constexpr char LoadFuncBodyEnum2[] = R"END( %s& val = (%s&)obj[i];
isOk = Load(&val, 1, serializer) && isOk;
)END";
constexpr char LoadFuncBodyEnd[] = R"END( }
constexpr char LoadFuncBodySetupEnd[] = R"END( }
// if we're not ok here, something went really wrong
assert(isOk);
return isOk;
}
// Failed to resolve hash, the type definition chaned since the file was saved! try to match by name.
*obj = {};
int32_t nameMatchIdx = serializer.TypeBuf.FindDefByName(typeName);
if (nameMatchIdx < 0)
{
// Name match failed, caller has to handle this and potentially skip some bytes
return false;
}
// Successfully matched name, but we need to follow the definition of the file now!
const Meta::TypeDef& matchedDef = serializer.TypeBuf.Defs[nameMatchIdx];
// Figure out new member mapping
uint64_t WriteDestinations[64];
for (int32_t i = 0; i < BX_COUNTOF(WriteDestinations); ++i)
{
WriteDestinations[i] = UINT64_MAX;
}
for (uint32_t i = 0; i < matchedDef.ChildCount; ++i)
{
const bx::StringView memberName = {&serializer.MemberNameBuf[matchedDef.MemberNameIndices[i].Offset], matchedDef.MemberNameIndices[i].Size};
const char* memberTypeName = serializer.TypeBuf.Defs[matchedDef.ChildIndices[i]].Name;
)END";
constexpr char LoadFuncBodyMemberCheck4[] =
R"END( if (bx::strCmp(memberName, "%s") == 0 && bx::strCmp(memberTypeName, "%s") == 0)
{
WriteDestinations[i] = offsetof(%s, %s);
}
)END";
constexpr char LoadFuncBodyTypeUpgradeStart[] = R"END( }
// Start reading in file order, skipping things that we don't know by name and type
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
uint8_t* objBasePtr = reinterpret_cast<uint8_t*>(&obj[i]);
for (uint32_t j = 0; j < matchedDef.ChildCount; ++j)
{
const Meta::TypeDef& childDef = serializer.TypeBuf.Defs[matchedDef.ChildIndices[j]];
const bx::StringView memberName = {&serializer.MemberNameBuf[matchedDef.MemberNameIndices[j].Offset], matchedDef.MemberNameIndices[j].Size};
if (WriteDestinations[j] == UINT64_MAX)
{
// Unknown member name or type changed
uint16_t count = bx::max(1, matchedDef.ChildArraySizes[matchedDef.ChildIndices[j]]);
serializer.Skip(childDef.Size * count);
continue;
}
)END";
constexpr char LoadFuncBodyTypeUpgradeMember3[] = R"END( if (bx::strCmp(memberName, "%s") == 0)
{
auto* fieldPtr = reinterpret_cast<%s*>(objBasePtr + WriteDestinations[j]);
uint16_t wantedCount = %u;
uint16_t existingCount = matchedDef.ChildArraySizes[j];
isOk = Load(fieldPtr, bx::min(wantedCount, existingCount), serializer) && isOk;
if (existingCount > wantedCount) serializer.Skip((existingCount - wantedCount) * childDef.Size);
continue;
}
)END";
constexpr char LoadFuncBodyTypeUpgradeEnd[] = R"END( assert(false);
}
}
assert(isOk);
return isOk;
}
)END";
constexpr char LoadFuncBodyNative[] = R"END( bool isOk = true;
for (int32_t i = 0; i < count; ++i)
{
isOk = serializer.Read(&obj[i], sizeof(obj[i])) && isOk;
}
return isOk;
}
)END";
constexpr char LoadFuncBodyEnum2[] = R"END( bool isOk = true;
for (int32_t i = 0; i < count; ++i)
{
%s& val = (%s&)obj[i];
isOk = Load(&val, 1, serializer) && isOk;
}
return isOk;
}
)END";
constexpr char MetadataStart1[] = R"END(
namespace Meta {
constexpr uint16_t CurrentMetaVersion = 1;
struct StrRef
{
uint16_t Offset;
uint16_t Size;
};
struct TypeDef
{
uint32_t Size = 0;
uint32_t Hash = 0;
char Name[64]{"Dummy"};
uint16_t ChildCount = 0;
uint16_t ChildIndices[64]{0};
uint16_t ChildArraySizes[64]{0};
StrRef MemberNameIndices[64]{0};
};
struct MetadataTable
{
TypeDef TypeDefinitions[%u]
{
)END";
// TODO: Sizeof is wrong!! we don't use padding!!
constexpr char MetadataTypeEntry7[] = R"END( TypeDef{sizeof(%s), %u, "%s", %u, {%s}, {%s}, {%s}},
)END";
constexpr char MetadataEnd1[] = R"END( };
char MemberNameBuffer[64*64*64]{"%s"};
};
constexpr MetadataTable Metadata;
}
)END";
} // namespace WriteTemplates
@@ -161,48 +318,69 @@ void CppFileWriter::WriteInternal(WriteBuffer& buf, const char* templateStr, va_
buf.WrittenBytes += bx::vsnprintf(&buf.Data[buf.WrittenBytes], BufferRequestSize, templateStr, args);
}
void CppFileWriter::PrintTypeName(char* buf,
int32_t bufSize,
const Def::FieldType& type,
const Def::DefinitionFile& definitions)
void CppFileWriter::PrintTypeName(
char* buf, int32_t bufSize, Def::TypeRef type, const Def::DefinitionFile& definitions, PrintFlags flags)
{
if (type.FieldKind == Def::EFieldType::Native)
if (buf == nullptr || bufSize == 0 || !IsValid(type))
{
if (int32_t(type.Native) < Generated::KnownType::EntryCount)
LOG_ERROR(0, "Invalid PrintTypeName call!");
return;
}
if (type.FieldKind == Def::EFieldType::DefinedClass)
{
bx::strCopy(buf, bufSize, Generated::KnownType::CName[size_t(type.Native)]);
auto& t = definitions.Types[type.TypeIdx];
if (((uint32_t)t.TypeFlags & (uint32_t)Def::ETypeFlags::IsNative) &&
((uint32_t)flags & (uint32_t)PrintFlags::PrintNativeTypeName))
{
bx::strCopy(buf, bufSize, t.NativeCName);
}
else
{
LOG_ERROR(0, "Unknown native type index: %u", type.Native);
bx::strCopy(buf, bufSize, t.Name);
}
}
else if (type.FieldKind == Def::EFieldType::DefinedClass)
{
bx::strCopy(buf, bufSize, definitions.Types[type.TypeIdx].Name);
}
else if (type.FieldKind == Def::EFieldType::DefinedEnum)
{
bx::strCopy(buf, bufSize, definitions.Enums[type.TypeIdx].Name);
if ((int32_t)flags & (int32_t)PrintFlags::PrintFullEnumName)
{
bx::strCat(buf, bufSize, "::Enum");
}
}
}
void CppFileWriter::WriteEnums(const Def::DefinitionFile& definitions)
{
for (int32_t enumIdx = 0; enumIdx < definitions.EnumCount; ++enumIdx)
{
const Def::Enum& e = definitions.Enums[enumIdx];
bool isFlagsEnum = (uint32_t)e.EnumFlags & (uint32_t)Def::EEnumFlags::FlagsEnum;
Write(WriteTemplates::EnumHeader4,
e.Name,
e.Hash,
e.EntryCount,
Generated::KnownType::CName[(int32_t)e.EnumType.Native]);
if (!IsValid(e.EnumType))
{
LOG_ERROR(0, "Invalid enum type (enum %i)", enumIdx);
continue;
}
char enumTypeNameBuf[Def::MaxNameLength]{0};
PrintTypeName(enumTypeNameBuf, sizeof(enumTypeNameBuf), e.EnumType, definitions);
Write(WriteTemplates::EnumHeader4, e.Name, definitions.TypeCount + enumIdx, e.EntryCount, enumTypeNameBuf);
for (int32_t entryIdx = 0; entryIdx < e.EntryCount; ++entryIdx)
{
if (isFlagsEnum)
{
char numBuf[64] = "0";
if (entryIdx > 0)
{
bx::snprintf(numBuf, sizeof(numBuf), "1 << %u", entryIdx - 1);
}
Write(WriteTemplates::EnumFieldNumbered2, e.EntryNames[entryIdx], numBuf);
}
else
{
Write(WriteTemplates::EnumField1, e.EntryNames[entryIdx]);
}
}
Write(WriteTemplates::EnumNamesStart2, "EntryNames", Def::MaxNameLength);
for (int32_t entryIdx = 0; entryIdx < e.EntryCount; ++entryIdx)
@@ -220,6 +398,23 @@ void CppFileWriter::WriteEnums(const Def::DefinitionFile& definitions)
}
Write(WriteTemplates::EnumNamesEnd);
if (isFlagsEnum)
{
Write(WriteTemplates::EnumFlagOperators12,
e.Name,
e.Name,
e.Name,
e.Name,
e.Name,
e.Name,
e.Name,
e.Name,
e.Name,
e.Name,
e.Name,
e.Name);
}
}
}
void CppFileWriter::WriteTypes(const Def::DefinitionFile& definitions)
@@ -227,15 +422,16 @@ void CppFileWriter::WriteTypes(const Def::DefinitionFile& definitions)
for (int32_t typeIdx = 0; typeIdx < definitions.TypeCount; ++typeIdx)
{
const Def::Type& t = definitions.Types[typeIdx];
if ((uint32_t)t.TypeFlags & (uint32_t)Def::ETypeFlags::IsNative) continue;
Write(WriteTemplates::StructHeader2, t.Name, t.Hash);
Write(WriteTemplates::StructHeader2, t.Name, typeIdx);
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{
char Type[64]{0};
PrintTypeName(Type, sizeof(Type), t.FieldTypes[fieldIdx], definitions);
char Array[32]{0};
uint32_t ArraySize = t.FieldArraySizes[fieldIdx];
if (ArraySize > 0)
if (ArraySize > 0 && ArraySize != UINT32_MAX)
{
bx::snprintf(Array, sizeof(Array), "[%u]", ArraySize);
}
@@ -251,6 +447,11 @@ void CppFileWriter::WriteSaveLoadMethods(const Def::DefinitionFile& definitions)
for (int32_t enumIdx = 0; enumIdx < definitions.EnumCount; ++enumIdx)
{
const Def::Enum& e = definitions.Enums[enumIdx];
if (!IsValid(e.EnumType))
{
LOG_ERROR(0, "Invalid enum!");
continue;
}
bx::snprintf(nameBuf, sizeof(nameBuf), "%s::Enum", e.Name);
PrintTypeName(fieldBuf, sizeof(fieldBuf), e.EnumType, definitions);
@@ -263,15 +464,23 @@ void CppFileWriter::WriteSaveLoadMethods(const Def::DefinitionFile& definitions)
WriteCpp(WriteTemplates::LoadFuncBodyStart1, nameBuf);
WriteCpp(WriteTemplates::LoadFuncBodyEnum2, fieldBuf, fieldBuf);
WriteCpp(WriteTemplates::LoadFuncBodyEnd);
}
for (int32_t typeIdx = 0; typeIdx < definitions.TypeCount; ++typeIdx)
for (uint16_t typeIdx = 0; typeIdx < definitions.TypeCount; ++typeIdx)
{
const Def::Type& t = definitions.Types[typeIdx];
Write(WriteTemplates::SaveFuncHeader1, t.Name);
Write(WriteTemplates::LoadFuncHeader1, t.Name);
char typeName[Def::MaxNameLength]{0};
PrintTypeName(typeName, sizeof(typeName), {typeIdx, Def::EFieldType::DefinedClass}, definitions);
WriteCpp(WriteTemplates::SaveFuncBodyStart1, t.Name);
Write(WriteTemplates::SaveFuncHeader1, typeName);
Write(WriteTemplates::LoadFuncHeader1, typeName);
WriteCpp(WriteTemplates::SaveFuncBodyStart1, typeName);
if ((int32_t)t.TypeFlags & (int32_t)Def::ETypeFlags::IsNative)
{
WriteCpp(WriteTemplates::SaveFuncBodyNative);
}
else
{
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{
WriteCpp(WriteTemplates::SaveFuncBodyType3,
@@ -279,19 +488,119 @@ void CppFileWriter::WriteSaveLoadMethods(const Def::DefinitionFile& definitions)
t.FieldNames[fieldIdx],
bx::max(1, t.FieldArraySizes[fieldIdx]));
}
}
WriteCpp(WriteTemplates::SaveFuncBodyEnd);
WriteCpp(WriteTemplates::LoadFuncBodyStart1, t.Name);
WriteCpp(WriteTemplates::LoadFuncBodyStart1, typeName);
if ((int32_t)t.TypeFlags & (int32_t)Def::ETypeFlags::IsNative)
{
WriteCpp(WriteTemplates::LoadFuncBodyNative);
}
else
{
WriteCpp(WriteTemplates::LoadFuncBodySetupStart2, typeName, typeName);
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{
WriteCpp(WriteTemplates::LoadFuncBodyType3,
WriteCpp(WriteTemplates::LoadFuncBodyQuickLoad2,
t.FieldArraySizes[fieldIdx] > 0 ? "" : "&",
t.FieldNames[fieldIdx],
bx::max(1, t.FieldArraySizes[fieldIdx]));
}
WriteCpp(WriteTemplates::LoadFuncBodyEnd);
WriteCpp(WriteTemplates::LoadFuncBodySetupEnd);
char fieldTypeName[Def::MaxNameLength]{0};
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{
const char* fieldName = t.FieldNames[fieldIdx];
// This prints native type name, seems wrong??
// Havent i already fixed this???
PrintTypeName(
fieldTypeName, sizeof(fieldTypeName), t.FieldTypes[fieldIdx], definitions, PrintFlags::None);
WriteCpp(WriteTemplates::LoadFuncBodyMemberCheck4, fieldName, fieldTypeName, typeName, fieldName);
}
WriteCpp(WriteTemplates::LoadFuncBodyTypeUpgradeStart);
for (uint32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{
const char* fieldName = t.FieldNames[fieldIdx];
PrintTypeName(fieldTypeName, sizeof(fieldTypeName), t.FieldTypes[fieldIdx], definitions);
WriteCpp(WriteTemplates::LoadFuncBodyTypeUpgradeMember3,
fieldName,
fieldTypeName,
bx::max(1, t.FieldArraySizes[fieldIdx]));
}
WriteCpp(WriteTemplates::LoadFuncBodyTypeUpgradeEnd);
}
}
}
namespace
{
char MemberNameBuffer[64 * 64 * 64]{0};
}
void CppFileWriter::WriteMetadata(const Def::DefinitionFile& definitions)
{
uint32_t memberNameBufferIdx = 0;
Write(WriteTemplates::MetadataStart1, definitions.TypeCount + definitions.EnumCount);
for (uint16_t i = 0; i < definitions.TypeCount; ++i)
{
auto& type = definitions.Types[i];
char fieldStr[256]{0};
for (int32_t j = 0; j < type.FieldCount; ++j)
{
if (j != 0) bx::strCat(fieldStr, sizeof(fieldStr), ", ");
char numBuf[8]{0};
int32_t idx = type.FieldTypes[j].TypeIdx;
if (type.FieldTypes[j].FieldKind == Def::EFieldType::DefinedEnum) idx += definitions.TypeCount;
bx::snprintf(numBuf, sizeof(numBuf), "%u", idx);
bx::strCat(fieldStr, sizeof(fieldStr), numBuf);
}
char typeStr[Def::MaxNameLength]{0};
PrintTypeName(typeStr, sizeof(typeStr), {i, Def::EFieldType::DefinedClass}, definitions);
char memberIdxStr[256]{0};
for (int32_t j = 0; j < type.FieldCount; ++j)
{
if (j != 0) bx::strCat(memberIdxStr, sizeof(memberIdxStr), ", ");
char numBuf[16]{0};
bx::snprintf(numBuf, sizeof(numBuf), "{%u, %u}", memberNameBufferIdx, bx::strLen(type.FieldNames[j]));
bx::strCat(memberIdxStr, sizeof(memberIdxStr), numBuf);
memberNameBufferIdx += bx::strCopy(&MemberNameBuffer[memberNameBufferIdx],
sizeof(MemberNameBuffer) - memberNameBufferIdx,
type.FieldNames[j]);
}
char arrStr[256]{0};
for (int32_t j = 0; j < type.FieldCount; ++j)
{
if (j != 0) bx::strCat(arrStr, sizeof(arrStr), ", ");
char numBuf[16]{0};
bx::snprintf(numBuf, sizeof(numBuf), "%u", type.FieldArraySizes[j]);
bx::strCat(arrStr, sizeof(arrStr), numBuf);
}
Write(WriteTemplates::MetadataTypeEntry7,
typeStr,
type.Hash,
type.Name,
type.FieldCount,
fieldStr,
arrStr,
memberIdxStr);
}
for (uint16_t i = 0; i < definitions.EnumCount; ++i)
{
auto& enumType = definitions.Enums[i];
char typeStr[Def::MaxNameLength]{0};
PrintTypeName(typeStr, sizeof(typeStr), {i, Def::EFieldType::DefinedEnum}, definitions);
Write(WriteTemplates::MetadataTypeEntry7, typeStr, enumType.Hash, enumType.Name, 0, "", "", "");
}
Write(WriteTemplates::MetadataEnd1, MemberNameBuffer);
}
void CppFileWriter::GenerateCpp(const bx::FilePath& outDir, const Def::DefinitionFile& definitions)
{
LOG(0, "Generating output files...");
@@ -304,6 +613,7 @@ void CppFileWriter::GenerateCpp(const bx::FilePath& outDir, const Def::Definitio
WriteEnums(definitions);
WriteTypes(definitions);
WriteSaveLoadMethods(definitions);
WriteMetadata(definitions);
Write(WriteTemplates::FileEnd);
WriteCpp(WriteTemplates::FileEnd);

View File

@@ -10,6 +10,13 @@ struct WriteBuffer
uint64_t WrittenBytes = 0;
};
enum class PrintFlags : uint32_t
{
None = 0,
PrintFullEnumName = 1 << 0,
PrintNativeTypeName = 1 << 1,
};
struct CppFileWriter
{
private:
@@ -21,10 +28,16 @@ struct CppFileWriter
void WriteInternal(WriteBuffer& buf, const char* templateStr, va_list args);
public:
void PrintTypeName(char* buf, int32_t bufSize, const Def::FieldType& type, const Def::DefinitionFile& definitions);
void PrintTypeName(char* buf,
int32_t bufSize,
Def::TypeRef type,
const Def::DefinitionFile& definitions,
PrintFlags flags = (PrintFlags)((uint32_t)PrintFlags::PrintFullEnumName |
(uint32_t)PrintFlags::PrintNativeTypeName));
void WriteEnums(const Def::DefinitionFile& definitions);
void WriteTypes(const Def::DefinitionFile& definitions);
void WriteSaveLoadMethods(const Def::DefinitionFile& definitions);
void WriteMetadata(const Def::DefinitionFile& definitions);
void GenerateCpp(const bx::FilePath& outDir, const Def::DefinitionFile& definitions);
void Write(const char* templateStr, ...);

View File

@@ -1,2 +0,0 @@
#pragma once
#include <cstdint>

View File

@@ -0,0 +1,260 @@
#include "Def.h"
#include "Generated.h"
namespace Gen
{
bool Save(const KnownType::Enum* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
auto val = (uint8_t)obj[i];
isOk = Save(&val, 1, serializer) && isOk;
}
return isOk;
}
bool Load(KnownType::Enum* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
uint8_t& val = (uint8_t&)obj[i];
isOk = Load(&val, 1, serializer) && isOk;
}
return isOk;
}
bool Save(const int8_t* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(int8_t* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const int16_t* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(int16_t* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const int32_t* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(int32_t* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const int64_t* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(int64_t* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const uint8_t* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(uint8_t* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const uint16_t* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(uint16_t* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const uint32_t* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(uint32_t* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const uint64_t* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(uint64_t* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const bool* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(bool* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const float* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(float* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const double* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(double* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const char* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Load(char* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
}
return isOk;
}
bool Save(const Test* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
isOk = serializer.Write(&obj[i], sizeof(obj[i])) && isOk;
}
return isOk;
}
bool Load(Test* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
isOk = serializer.Read(&obj[i], sizeof(obj[i])) && isOk;
}
return isOk;
}
bool Save(const Texture* obj, uint32_t count, Serializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
isOk = serializer.Write(&obj[i], sizeof(obj[i])) && isOk;
isOk = serializer.Write(&obj[i], sizeof(obj[i])) && isOk;
isOk = serializer.Write(&obj[i], sizeof(obj[i])) && isOk;
isOk = Save(&obj[i].T, 1, serializer) && isOk;
}
return isOk;
}
bool Load(Texture* obj, uint32_t count, Deserializer& serializer)
{
bool isOk = true;
for (uint32_t i = 0; i < count; ++i)
{
isOk = serializer.Read(&obj[i], sizeof(obj[i])) && isOk;
isOk = serializer.Read(&obj[i], sizeof(obj[i])) && isOk;
isOk = serializer.Read(&obj[i], sizeof(obj[i])) && isOk;
isOk = Load(&obj[i].T, 1, serializer) && isOk;
}
return isOk;
}
}

View File

@@ -1,12 +1,15 @@
#pragma once
#include "Def.h"
#include <cstdint>
namespace Generated
namespace Gen
{
struct Serializer;
struct Deserializer;
struct KnownType
{
static constexpr uint16_t EnumIdx = 0;
static constexpr int32_t EntryCount = 12;
enum class Enum : int32_t
enum Enum : uint8_t
{
i8,
i16,
@@ -54,13 +57,89 @@ namespace Generated
};
struct Test
{
static constexpr uint16_t TypeIdx = 12;
uint32_t Number = {};
};
struct Texture
{
static constexpr uint16_t TypeIdx = 13;
uint32_t Width = {};
uint32_t Height = {};
char StrTest[3] = {};
Test T = {};
};
bool Save(const KnownType::Enum* obj, uint32_t count, Serializer& serializer);
bool Load(KnownType::Enum* obj, uint32_t count, Deserializer& serializer);
bool Save(const int8_t* obj, uint32_t count, Serializer& serializer);
bool Load(int8_t* obj, uint32_t count, Deserializer& serializer);
bool Save(const int16_t* obj, uint32_t count, Serializer& serializer);
bool Load(int16_t* obj, uint32_t count, Deserializer& serializer);
bool Save(const int32_t* obj, uint32_t count, Serializer& serializer);
bool Load(int32_t* obj, uint32_t count, Deserializer& serializer);
bool Save(const int64_t* obj, uint32_t count, Serializer& serializer);
bool Load(int64_t* obj, uint32_t count, Deserializer& serializer);
bool Save(const uint8_t* obj, uint32_t count, Serializer& serializer);
bool Load(uint8_t* obj, uint32_t count, Deserializer& serializer);
bool Save(const uint16_t* obj, uint32_t count, Serializer& serializer);
bool Load(uint16_t* obj, uint32_t count, Deserializer& serializer);
bool Save(const uint32_t* obj, uint32_t count, Serializer& serializer);
bool Load(uint32_t* obj, uint32_t count, Deserializer& serializer);
bool Save(const uint64_t* obj, uint32_t count, Serializer& serializer);
bool Load(uint64_t* obj, uint32_t count, Deserializer& serializer);
bool Save(const bool* obj, uint32_t count, Serializer& serializer);
bool Load(bool* obj, uint32_t count, Deserializer& serializer);
bool Save(const float* obj, uint32_t count, Serializer& serializer);
bool Load(float* obj, uint32_t count, Deserializer& serializer);
bool Save(const double* obj, uint32_t count, Serializer& serializer);
bool Load(double* obj, uint32_t count, Deserializer& serializer);
bool Save(const char* obj, uint32_t count, Serializer& serializer);
bool Load(char* obj, uint32_t count, Deserializer& serializer);
bool Save(const Test* obj, uint32_t count, Serializer& serializer);
bool Load(Test* obj, uint32_t count, Deserializer& serializer);
bool Save(const Texture* obj, uint32_t count, Serializer& serializer);
bool Load(Texture* obj, uint32_t count, Deserializer& serializer);
namespace Meta {
struct TypeDef
{
uint32_t Size = 0;
uint32_t Hash = 0;
char Name[64]{"Dummy"};
uint16_t ChildCount = 0;
uint16_t ChildIndices[64]{0};
};
struct EnumDef
{
uint32_t Size = 0;
uint32_t Hash = 0;
};
struct MetadataTable
{
TypeDef TypeDefinitions[14]
{
TypeDef{sizeof(int8_t), 0, "i8", 0, {}},
TypeDef{sizeof(int16_t), 1, "i16", 0, {}},
TypeDef{sizeof(int32_t), 2, "i32", 0, {}},
TypeDef{sizeof(int64_t), 3, "i64", 0, {}},
TypeDef{sizeof(uint8_t), 4, "u8", 0, {}},
TypeDef{sizeof(uint16_t), 5, "u16", 0, {}},
TypeDef{sizeof(uint32_t), 6, "u32", 0, {}},
TypeDef{sizeof(uint64_t), 7, "u64", 0, {}},
TypeDef{sizeof(bool), 8, "b", 0, {}},
TypeDef{sizeof(float), 9, "f32", 0, {}},
TypeDef{sizeof(double), 10, "f64", 0, {}},
TypeDef{sizeof(char), 11, "str", 0, {}},
TypeDef{sizeof(Test), 273256278, "Test", 1, {6}},
TypeDef{sizeof(Texture), 992460010, "Texture", 4, {6, 6, 11, 12}},
};
EnumDef EnumDefinitions[1]
{
EnumDef{sizeof(KnownType::Enum), 2983807453},
};
};
constexpr MetadataTable Metadata;
}
}

View File

@@ -2,8 +2,8 @@
#include "Gen/Generated.h"
#include "Logging.h"
#include "MiniDef.h"
#include "TypeDef.h"
#include "bx/hash.h"
#include "bx/string.h"
#include <bx/filepath.h>
@@ -114,6 +114,8 @@ Parser::Result Parser::Parse()
{
ReadPtr = &Buffer[0];
Parser::Result Res = Parser::OK;
CHECK(LoadNativeTypes());
while (Res == Parser::OK)
{
Res = SkipWhitespace();
@@ -136,6 +138,7 @@ Parser::Result Parser::Parse()
Parser::Result Parser::HandleFileStart()
{
Result Res = OK;
if (CmpAdvance("type", Res, true) && Res == OK)
{
return HandleType();
@@ -152,6 +155,20 @@ Parser::Result Parser::HandleFileStart()
return Error;
}
Parser::Result Parser::LoadNativeTypes()
{
for (int32_t i = 0; i < Gen::KnownType::EntryCount; ++i)
{
auto& t = Definitions.Types[i];
t.Hash = i; // TODO!
bx::strCopy(t.Name, sizeof(t.Name), Gen::KnownType::EntryNames[i]);
bx::strCopy(t.NativeCName, sizeof(t.NativeCName), Gen::KnownType::CName[i]);
t.TypeFlags = Def::ETypeFlags::IsNative;
++Definitions.TypeCount;
}
return OK;
}
Parser::Result Parser::HandleType()
{
Result Res = OK;
@@ -200,6 +217,13 @@ Parser::Result Parser::HandleType()
CHECK(ReadUint(t.FieldArraySizes[t.FieldCount]));
CHECK(ExpectChar(")"));
}
else if (CmpAdvance("ArrDynamic", Res, true) && Res == Result::OK)
{
CHECK(ExpectChar("("));
CHECK(ReadUint(t.FieldArraySizes[t.FieldCount]));
t.FieldFlags[t.FieldCount] = Def::ETypeFieldFlags::DynamicArray;
CHECK(ExpectChar(")"));
}
else if (CmpAdvance("Default", Res, true) && Res == OK)
{
CHECK(ExpectChar("("));
@@ -249,12 +273,21 @@ Parser::Result Parser::HandleEnum()
if (CmpAdvance("(", Res) && Res == OK)
{
CHECK(SkipWhitespace());
Def::FieldType field;
CHECK(ReadNativeFieldType(field));
CHECK(ReadDefinedFieldType(e.EnumType));
CHECK(SkipWhitespace());
CHECK(ExpectChar(")"));
CHECK(SkipWhitespace());
}
else
{
e.EnumType = {(uint16_t)Gen::KnownType::i32, Def::EFieldType::DefinedClass};
}
if (CmpAdvance("Flags", Res) && Res == OK)
{
e.EnumFlags = (Def::EEnumFlags)((uint32_t)e.EnumFlags | (uint32_t)Def::EEnumFlags::FlagsEnum);
CHECK(SkipWhitespace());
}
CHECK(ExpectChar("{"));
CHECK(SkipWhitespace());
@@ -338,29 +371,13 @@ Parser::Result Parser::ReadTypeToken()
{
Result Res = OK;
Def::Type& t = Definitions.Types[Definitions.TypeCount];
if (ReadNativeFieldType(t.FieldTypes[t.FieldCount]) == OK) return OK;
if (ReadDefinedFieldType(t.FieldTypes[t.FieldCount]) == OK) return OK;
LOG_ERROR(Line, "Unknown type token!");
ErrorLine();
return Error;
}
Parser::Result Parser::ReadNativeFieldType(Def::FieldType& FieldT)
{
Result Res = OK;
for (int32_t i = 0; i < Generated::KnownType::EntryCount; ++i)
{
if (CmpAdvance(Generated::KnownType::EntryNames[i], Res, true) && Res == OK)
{
FieldT.FieldKind = Def::EFieldType::Native;
FieldT.Native = Generated::KnownType::Enum(i);
return OK;
}
}
return Error;
}
Parser::Result Parser::ReadDefinedFieldType(Def::FieldType& FieldT)
Parser::Result Parser::ReadDefinedFieldType(Def::TypeRef& FieldT)
{
Result Res = OK;
for (uint16_t i = 0; i < Definitions.TypeCount; ++i)
@@ -437,11 +454,7 @@ uint32_t Parser::CalculateTypeHash(const Def::Type& t)
hash.add(t.FieldArraySizes[i]);
Def::EFieldType fieldType = t.FieldTypes[i].FieldKind;
if (fieldType == Def::EFieldType::Native)
{
hash.add(t.FieldTypes[i].Native);
}
else if (fieldType == Def::EFieldType::DefinedClass)
if (fieldType == Def::EFieldType::DefinedClass)
{
Def::Type& dependType = Definitions.Types[t.FieldTypes[i].TypeIdx];
if (dependType.Hash == 0)
@@ -456,7 +469,7 @@ uint32_t Parser::CalculateTypeHash(const Def::Type& t)
}
else
{
LOG_ERROR(0, "TODO!");
LOG_ERROR(Line, "TODO!");
}
}
return hash.end();
@@ -466,9 +479,14 @@ void Parser::CalculateHashes()
for (int32_t i = 0; i < Definitions.EnumCount; ++i)
{
Def::Enum& e = Definitions.Enums[i];
if (!IsValid(e.EnumType))
{
LOG_ERROR(0, "Invalid enum type at idx %i", i);
continue;
}
bx::HashMurmur2A hash;
hash.begin();
hash.add(e.EnumType.Native);
hash.add(Definitions.Types[e.EnumType.TypeIdx].Hash); // TODO: add enum entries?
e.Hash = hash.end();
}
for (int32_t i = 0; i < Definitions.TypeCount; ++i)

View File

@@ -86,12 +86,12 @@ class Parser
Result ExpectChar(bx::StringView expect);
Result Advance(int32_t Amount);
Result HandleFileStart();
Result LoadNativeTypes();
Result HandleType();
Result HandleEnum();
Result ReadName(char* Target);
Result ReadUint(uint32_t& Out);
Result ReadNativeFieldType(Def::FieldType& FieldT);
Result ReadDefinedFieldType(Def::FieldType& FieldT);
Result ReadDefinedFieldType(Def::TypeRef& FieldT);
Result ReadTypeToken();
Result ReadOptionalEnumValues(Def::Enum& Enum, int32_t EntryIdx);

View File

@@ -1,5 +1,4 @@
#pragma once
#include "Gen/Generated.h"
#include <cstdint>
namespace Def
@@ -7,39 +6,65 @@ namespace Def
constexpr int32_t MaxNameLength = 64;
constexpr int32_t MaxFields = 64;
constexpr int32_t MaxExtraEnumFields = 2;
enum class EFieldType
enum class EFieldType : uint8_t
{
Native,
DefinedClass,
DefinedEnum
};
struct FieldType
enum class ETypeFlags : uint32_t
{
EFieldType FieldKind = EFieldType::Native;
Generated::KnownType::Enum Native = Generated::KnownType::Enum::i32;
uint16_t TypeIdx = UINT16_MAX;
None = 0,
IsNative = 1 << 0,
};
enum class ETypeFieldFlags : uint32_t
{
None = 0,
DynamicArray = 1 << 0,
};
enum class EEnumFlags : uint32_t
{
None = 0,
FlagsEnum = 1,
};
struct TypeRef
{
uint16_t TypeIdx = UINT16_MAX;
EFieldType FieldKind = EFieldType::DefinedClass;
};
inline bool IsValid(TypeRef ref)
{
return ref.TypeIdx != UINT16_MAX;
}
struct Type
{
int32_t FieldCount = 0;
FieldType FieldTypes[MaxFields];
TypeRef FieldTypes[MaxFields];
char FieldNames[MaxFields][MaxNameLength];
uint32_t FieldArraySizes[MaxFields]{0};
char FieldValues[MaxFields][128]{0};
ETypeFlags TypeFlags = ETypeFlags::None;
ETypeFieldFlags FieldFlags[MaxFields]{ETypeFieldFlags::None};
char Name[MaxNameLength]{0};
char NativeCName[MaxNameLength]{0};
uint32_t Hash = 0;
};
struct Enum
{
FieldType EnumType;
TypeRef EnumType;
int32_t EntryCount = 0;
char EntryNames[MaxFields][MaxNameLength];
int32_t ExtraStringFieldCount = 0;
char ExtraStringFieldNames[MaxExtraEnumFields][MaxNameLength];
char ExtraStringFields[MaxFields][MaxExtraEnumFields][MaxNameLength];
char Name[MaxNameLength]{0};
EEnumFlags EnumFlags = EEnumFlags::None;
uint32_t Hash = 0;
};

View File

@@ -96,16 +96,19 @@ struct SharedDevData
char ShaderLog[2048]{0};
};
struct MemoryArena
{
uint8_t* Base = nullptr;
uint64_t Used = 0;
uint64_t MaxSize = 0;
uint64_t LastAllocSize = 0;
};
struct GameData
{
void* PermanentStorage = nullptr;
uint64_t PermanentStorageSize = 0;
void* EntityStorage = nullptr;
uint64_t EntityStorageSize = 0;
void* TransientStorage = nullptr;
uint64_t TransientStorageSize = 0;
MemoryArena PermanentArena;
MemoryArena EntityArena;
MemoryArena TransientArena;
};
struct SharedData

View File

@@ -23,6 +23,10 @@ constexpr const char* DLLPath = "libPuzGame.dll";
constexpr const wchar_t* DLLWatch = L"libPuzGame2.dll";
#endif
constexpr uint64_t KB = 1024LLU;
constexpr uint64_t MB = 1024LLU * 1024LLU;
constexpr uint64_t GB = 1024LLU * 1024LLU * 1024LLU;
namespace
{
bx::AllocatorI* defaultAllocator = new bx::DefaultAllocator{};
@@ -98,6 +102,10 @@ void FileChangeCheck(HANDLE dirHandle, FileChangeType changeType, const wchar_t*
{
if (compName == nullptr || compName[0] == 0 || wcscmp(notifyData->FileName, compName) == 0)
{
char buf[1024]{0};
wcstombs(buf, notifyData->FileName, sizeof(buf));
printf("file change of type %u: %s\n", changeType, buf);
FileChangeNotification notif;
wcscpy(notif.FileName, notifyData->FileName);
if (changeType == FileChangeType::DLL)
@@ -106,23 +114,25 @@ void FileChangeCheck(HANDLE dirHandle, FileChangeType changeType, const wchar_t*
}
else if (changeType == FileChangeType::Shader)
{
std::system("shadercompile.bat > shadercompile.log");
printf("shader source change!\n");
std::system("powershell.exe .\\shadercompile.ps1 > shadercompile.log");
Sleep(1000);
std::ifstream shaderLogFile("shadercompile.log");
if (shaderLogFile.is_open())
{
shaderLogFile.seekg(0, std::ios::end);
uint32_t size = bx::min(BX_COUNTOF(Shared.Dev.ShaderLog), shaderLogFile.tellg());
uint32_t size = bx::min(BX_COUNTOF(Shared.Dev.ShaderLog) - 1, shaderLogFile.tellg());
shaderLogFile.seekg(0);
shaderLogFile.read(Shared.Dev.ShaderLog, size);
shaderLogFile.close();
Shared.Dev.ShaderLog[size] = 0;
}
}
else if (changeType == FileChangeType::CompiledShader)
{
printf("compiled shader change!\n");
DevData.FileWatcher.ShaderQueue.push(&notif);
}
printf("detected file change of type %u!\n", changeType);
}
}
@@ -254,6 +264,15 @@ bool ReloadDLL()
return true;
}
void InitMemoryArena(MemoryArena& arena, uint64_t size)
{
arena.MaxSize = size;
arena.Base = reinterpret_cast<uint8_t*>(
VirtualAllocEx(GetCurrentProcess(), NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
arena.Used = 0;
arena.LastAllocSize = 0;
}
int main()
{
char PathBuf[512]{0};
@@ -273,7 +292,12 @@ int main()
DevData.FileWatcher.ShaderWatcher.ChangeType = FileChangeType::Shader;
DevData.FileWatcher.CompiledShaderWatcher.ChangeType = FileChangeType::CompiledShader;
bx::strCopy(DevData.FileWatcher.DLLWatcher.DirPath, sizeof(DevData.FileWatcher.DLLWatcher.DirPath), "cmake-build");
char exePath[1024];
GetModuleFileName(nullptr, exePath, BX_COUNTOF(exePath));
bx::FilePath exeFilePath{exePath};
bx::FilePath exeDir{exeFilePath.getPath()};
bx::strCopy(
DevData.FileWatcher.DLLWatcher.DirPath, sizeof(DevData.FileWatcher.DLLWatcher.DirPath), exeDir.getCPtr());
bx::strCopy(
DevData.FileWatcher.ShaderWatcher.DirPath, sizeof(DevData.FileWatcher.ShaderWatcher.DirPath), "game/shaders");
bx::strCopy(DevData.FileWatcher.CompiledShaderWatcher.DirPath,
@@ -290,15 +314,9 @@ int main()
HANDLE compiledShaderThread =
CreateThread(NULL, 0, FileWatcherThread, &DevData.FileWatcher.CompiledShaderWatcher, 0, &fileWatcherThreadId);
Shared.Game.PermanentStorageSize = 1024 * 1024;
Shared.Game.PermanentStorage = VirtualAllocEx(
GetCurrentProcess(), NULL, Shared.Game.PermanentStorageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Shared.Game.EntityStorageSize = 1024 * 1024;
Shared.Game.EntityStorage = VirtualAllocEx(
GetCurrentProcess(), NULL, Shared.Game.EntityStorageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Shared.Game.TransientStorageSize = 1024 * 1024 * 100;
Shared.Game.TransientStorage = VirtualAllocEx(
GetCurrentProcess(), NULL, Shared.Game.TransientStorageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
InitMemoryArena(Shared.Game.PermanentArena, MB);
InitMemoryArena(Shared.Game.EntityArena, MB);
InitMemoryArena(Shared.Game.TransientArena, 2 * GB);
StartupFunc(Shared);
bool isRunning = true;

158
src/game/Entity.h Normal file
View File

@@ -0,0 +1,158 @@
#pragma once
#include "../gen/Generated.h"
#include "Log.h"
#include "Puzzle.h" // TODO: remove
#include "bgfx/bgfx.h"
#include "rendering/Rendering.h"
#include <cstdint>
#include <typeinfo>
#define ENTITY_HANDLE(X) \
struct X \
{ \
uint16_t Idx = UINT16_MAX; \
}; \
inline bool IsValid(X h) \
{ \
return h.Idx != UINT16_MAX; \
}
namespace Game
{
int32_t GetNextRenderID();
struct EntityRenderData
{
Gen::Vec4 DotColor{1.0f, 1.0f, 1.0f, 1.0f};
Gen::Vec4 BaseColor{0.0f, 0.0f, 0.0f, 1.0f};
Gen::Transform Transform;
Gen::EMaterial::Enum MaterialHandle = Gen::EMaterial::UI;
Gen::TextureHandle TextureHandle;
Gen::ModelHandle ModelH;
bool Visible = true;
int32_t RenderID = 0;
void Render(const Model* models, const Material* materials, const Texture* textures);
void LoadFromSaved(const Gen::SavedEntityRenderData& saved);
};
ENTITY_HANDLE(CubeHandle);
struct Cube
{
int32_t TestX = -1;
int32_t TestY = -1;
EntityRenderData EData;
void Setup();
void Update();
};
ENTITY_HANDLE(TestEntityHandle);
struct TestEntity
{
EntityRenderData EData;
void Setup();
};
ENTITY_HANDLE(PuzzleTileEntityHandle);
struct PuzzleTileEntity
{
EntityRenderData EData;
};
ENTITY_HANDLE(PuzzleTileCoverHandle);
struct PuzzleTileCover
{
EntityRenderData EData;
};
ENTITY_HANDLE(UIQuadEntityHandle);
struct UIQuadEntity
{
EntityRenderData EData;
Gen::Vec3 UIPos;
float UIRot = 0.0f;
};
ENTITY_HANDLE(LevelEntityHandle);
struct LevelEntity
{
EntityRenderData EData;
};
class IEntityManager
{
public:
virtual bool Setup(uint8_t*& ptr, bool forceReset) = 0;
};
template <typename T, typename HandleT, uint32_t C> class EntityManager : public IEntityManager
{
public:
uint16_t Count = 0;
T* Data = nullptr;
uint32_t EntitySize = 0;
T InvalidObject{};
bool IsEnabled = true;
public:
// Returns true if size changed
bool Setup(uint8_t*& ptr, bool forceReset)
{
bool changed = false;
if (EntitySize != sizeof(T) || forceReset)
{
Count = 0;
changed = true;
}
EntitySize = sizeof(T);
Data = reinterpret_cast<T*>(ptr);
ptr += C * EntitySize;
return changed;
}
HandleT New()
{
if (Data == nullptr)
{
ERROR_ONCE("Accessed EntityManager before setup!");
return {};
}
if (Count >= C)
{
ERROR_ONCE("Too many entities!");
return {};
}
Data[Count] = {};
Data[Count].EData.RenderID = GetNextRenderID();
HandleT H;
H.Idx = Count;
++Count;
return H;
}
T& Get(HandleT handle)
{
if (handle.Idx >= Count)
{
ERROR_ONCE("OOB Access!");
return InvalidObject;
}
return Data[handle.Idx];
}
void Render(const Model* models, const Material* materials, const Texture* textures)
{
if (!IsEnabled) return;
bgfx::setMarker(typeid(T).name());
for (uint16_t i = 0; i < Count; ++i)
{
Get({i}).EData.Render(models, materials, textures);
}
}
};
typedef EntityManager<UIQuadEntity, UIQuadEntityHandle, Puzzle::Config::MaxTilesInPuzzle * 2> UIQuadEntityManager;
} // namespace Game

515
src/game/Gen.cpp Normal file
View File

@@ -0,0 +1,515 @@
#include "Gen.h"
#include "bx/math.h"
namespace Gen
{
bool IsValid(const ModelHandle& h)
{
return h.ModelIdx != UINT16_MAX && IsValid(h.Asset);
}
bool IsValid(const AssetHandle& h)
{
return h.Idx != UINT32_MAX;
}
bool IsValid(const TextureHandle& h)
{
return h.TextureIdx != UINT16_MAX && IsValid(h.Asset);
}
bool operator==(const AssetHandle& lhs, const AssetHandle& rhs)
{
return lhs.Idx == rhs.Idx;
}
bool operator==(const ModelHandle& lhs, const ModelHandle& rhs)
{
if (!IsValid(lhs) || !IsValid(rhs)) return IsValid(lhs) == IsValid(rhs);
return lhs.ModelIdx == rhs.ModelIdx && lhs.Asset == rhs.Asset;
}
PuzPos operator+=(PuzPos lhs, const PuzPos& rhs)
{
lhs.X += rhs.X;
lhs.Y += rhs.Y;
return lhs;
}
PuzPos operator+(PuzPos lhs, const PuzPos& rhs)
{
PuzPos res = lhs;
res.X += rhs.X;
res.Y += rhs.Y;
return res;
}
PuzPos operator-=(PuzPos lhs, const PuzPos& rhs)
{
lhs.X -= rhs.X;
lhs.Y -= rhs.Y;
return lhs;
}
PuzPos operator-(PuzPos lhs, const PuzPos& rhs)
{
PuzPos res = lhs;
res.X += rhs.X;
res.Y += rhs.Y;
return res;
}
// Vec4
Vec4& operator+=(Vec4& lhs, const Vec4& rhs)
{
lhs.x += rhs.x;
lhs.y += rhs.y;
lhs.z += rhs.z;
lhs.w += rhs.w;
return lhs;
}
Vec4 operator+(const Vec4& lhs, const Vec4& rhs)
{
Vec4 out = lhs;
return out += rhs;
}
Vec4& operator+=(Vec4& lhs, float rhs)
{
lhs.x += rhs;
lhs.y += rhs;
lhs.z += rhs;
lhs.w += rhs;
return lhs;
}
Vec4 operator+(Vec4& lhs, float rhs)
{
Vec4 out = lhs;
return out += rhs;
}
Vec4& operator-=(Vec4& lhs, const Vec4& rhs)
{
lhs.x -= rhs.x;
lhs.y -= rhs.y;
lhs.z -= rhs.z;
lhs.w -= rhs.w;
return lhs;
}
Vec4 operator-(const Vec4& lhs, const Vec4& rhs)
{
Vec4 out = lhs;
return out -= rhs;
}
Vec4& operator-=(Vec4& lhs, float rhs)
{
lhs.x -= rhs;
lhs.y -= rhs;
lhs.z -= rhs;
lhs.w -= rhs;
return lhs;
}
Vec4 operator-(const Vec4& lhs, float rhs)
{
Vec4 out = lhs;
return out -= rhs;
}
Vec4& operator*=(Vec4& lhs, float rhs)
{
lhs.x *= rhs;
lhs.y *= rhs;
lhs.z *= rhs;
lhs.w *= rhs;
return lhs;
}
Vec4 operator*(const Vec4& lhs, float rhs)
{
Vec4 out = lhs;
return out *= rhs;
}
Vec4& operator/=(Vec4& lhs, float rhs)
{
lhs.x /= rhs;
lhs.y /= rhs;
lhs.z /= rhs;
lhs.w /= rhs;
return lhs;
}
Vec4 operator/(const Vec4& lhs, float rhs)
{
Vec4 out = lhs;
return out /= rhs;
}
// Vec3
Vec3& operator+=(Vec3& lhs, const Vec3& rhs)
{
lhs.x += rhs.x;
lhs.y += rhs.y;
lhs.z += rhs.z;
return lhs;
}
Vec3 operator+(const Vec3& lhs, const Vec3& rhs)
{
Vec3 out = lhs;
return out += rhs;
}
Vec3& operator+=(Vec3& lhs, float rhs)
{
lhs.x += rhs;
lhs.y += rhs;
lhs.z += rhs;
return lhs;
}
Vec3 operator+(Vec3& lhs, float rhs)
{
Vec3 out = lhs;
return out += rhs;
}
Vec3& operator-=(Vec3& lhs, const Vec3& rhs)
{
lhs.x -= rhs.x;
lhs.y -= rhs.y;
lhs.z -= rhs.z;
return lhs;
}
Vec3 operator-(const Vec3& lhs, const Vec3& rhs)
{
Vec3 out = lhs;
return out -= rhs;
}
Vec3& operator-=(Vec3& lhs, float rhs)
{
lhs.x -= rhs;
lhs.y -= rhs;
lhs.z -= rhs;
return lhs;
}
Vec3 operator-(const Vec3& lhs, float rhs)
{
Vec3 out = lhs;
return out -= rhs;
}
Vec3& operator*=(Vec3& lhs, float rhs)
{
lhs.x *= rhs;
lhs.y *= rhs;
lhs.z *= rhs;
return lhs;
}
Vec3 operator*(const Vec3& lhs, float rhs)
{
Vec3 out = lhs;
return out *= rhs;
}
Vec3& operator/=(Vec3& lhs, float rhs)
{
lhs.x /= rhs;
lhs.y /= rhs;
lhs.z /= rhs;
return lhs;
}
Vec3 operator/(const Vec3& lhs, float rhs)
{
Vec3 out = lhs;
return out /= rhs;
}
// Vec2
Vec2& operator+=(Vec2& lhs, const Vec2& rhs)
{
lhs.x += rhs.x;
lhs.y += rhs.y;
return lhs;
}
Vec2 operator+(const Vec2& lhs, const Vec2& rhs)
{
Vec2 out = lhs;
return out += rhs;
}
Vec2& operator+=(Vec2& lhs, float rhs)
{
lhs.x += rhs;
lhs.y += rhs;
return lhs;
}
Vec2 operator+(Vec2& lhs, float rhs)
{
Vec2 out = lhs;
return out += rhs;
}
Vec2& operator-=(Vec2& lhs, const Vec2& rhs)
{
lhs.x -= rhs.x;
lhs.y -= rhs.y;
return lhs;
}
Vec2 operator-(const Vec2& lhs, const Vec2& rhs)
{
Vec2 out = lhs;
return out -= rhs;
}
Vec2& operator-=(Vec2& lhs, float rhs)
{
lhs.x -= rhs;
lhs.y -= rhs;
return lhs;
}
Vec2 operator-(const Vec2& lhs, float rhs)
{
Vec2 out = lhs;
return out -= rhs;
}
Vec2& operator*=(Vec2& lhs, float rhs)
{
lhs.x *= rhs;
lhs.y *= rhs;
return lhs;
}
Vec2 operator*(const Vec2& lhs, float rhs)
{
Vec2 out = lhs;
return out *= rhs;
}
Vec2& operator/=(Vec2& lhs, float rhs)
{
lhs.x /= rhs;
lhs.y /= rhs;
return lhs;
}
Vec2 operator/(const Vec2& lhs, float rhs)
{
Vec2 out = lhs;
return out /= rhs;
}
Mat4 Inverse(const Mat4& mat)
{
Mat4 result;
bx::mtxInverse(result.M, &mat.M[0]);
return result;
}
Mat4 Transpose(const Mat4& mat)
{
Mat4 result;
bx::mtxTranspose(result.M, &mat.M[0]);
return result;
}
Vec4 Mul(const Mat4& mat, const Vec4& vec)
{
Vec4 out;
bx::vec4MulMtx(&out.x, &vec.x, &mat.M[0]);
return out;
}
Vec3 EulerFromRotation(const Mat4& rotation)
{
const float r32 = rotation.M[9];
const float r33 = rotation.M[10];
float x = bx::atan2(r32, r33);
float y = bx::atan2(-rotation.M[8], bx::sqrt(r32 * r32 + r33 * r33));
float z = bx::atan2(rotation.M[4], rotation.M[0]);
return {x, y, z};
}
Mat4 RotationFromEuler(const Vec3& euler)
{
Mat4 mat;
bx::Quaternion quat = bx::fromEuler({euler.x, euler.y, euler.z});
bx::mtxFromQuaternion(mat.M, quat);
return mat;
}
Mat4 RotationFromQuaternion(const Vec4& quaternion)
{
Mat4 mat;
bx::Quaternion quat{quaternion.x, quaternion.y, quaternion.z, quaternion.w};
bx::mtxFromQuaternion(mat.M, quat);
return mat;
}
float Magnitude(const Vec4& vec)
{
return bx::sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z + vec.w * vec.w);
}
float Magnitude(const Vec3& vec)
{
return bx::sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z);
}
float Magnitude(const Vec2& vec)
{
return bx::sqrt(vec.x * vec.x + vec.y * vec.y);
}
Vec4 Normalized(const Vec4& vec)
{
Vec4 res = vec;
return res /= Magnitude(vec);
}
Vec3 Normalized(const Vec3& vec)
{
Vec3 res = vec;
return res /= Magnitude(vec);
}
Vec2 Normalized(const Vec2& vec)
{
Vec2 res = vec;
return res /= Magnitude(vec);
}
float DotProduct(Vec3 a, Vec3 b)
{
return a.x * b.x + a.y * b.y + a.z * b.z;
}
Vec3 CrossProduct(Vec3 a, Vec3 b)
{
float x = a.y * b.z - a.z * b.y;
float y = a.z * b.x - a.x * b.z;
float z = a.x * b.y - a.y * b.x;
return {x, y, z};
}
Vec3 CrossProductFromPlane(Vec3 a, Vec3 b, Vec3 c)
{
// TODO: normalize might not be necessary
Vec3 lineA = Normalized(b - a);
Vec3 lineB = Normalized(c - a);
return CrossProduct(lineA, lineB);
}
bool RayPlaneIntersect(Vec3 l1, Vec3 l2, Vec3 p1, Vec3 p2, Vec3 p3, Vec3& out)
{
// thanks to Paul Bourke and Bryan Hanson
// l1,l2 constitute the line. P1,P2,P3 constitute the plane
out = {};
Vec3 N = CrossProductFromPlane(p1, p2, p3); // N is the normal of the plane
float n = DotProduct(N, Vec3{p3.x - l1.x, p3.y - l1.y, p3.z - l1.z});
Vec3 LbMinusLa = Vec3{l2.x - l1.x, l2.y - l1.y, l2.z - l1.z};
float d = DotProduct(N, LbMinusLa);
if (d == 0) return false; // Line is parallel to or in the plane
float u = n / d;
if ((u >= 0.0) && (u <= 1.0))
{ // Plane is between the two points
} // can be used for checking but does not influence the outcome
out = Vec3{l1.x + u * LbMinusLa.x, l1.y + u * LbMinusLa.y, l1.z + u * LbMinusLa.z};
return true;
}
bool RayTriangleIntersect(Vec3 l1, Vec3 l2, Vec3 p1, Vec3 p2, Vec3 p3, Vec3& out)
{
const float EPSILON = 1e-6f;
Vec3 dir = l2 - l1; // Ray direction
Vec3 edge1 = p2 - p1;
Vec3 edge2 = p3 - p1;
Vec3 h = CrossProduct(dir, edge2);
float a = DotProduct(edge1, h);
if (bx::abs(a) < EPSILON) return false; // Ray is parallel to the triangle
float f = 1.0f / a;
Vec3 s = l1 - p1;
float u = f * DotProduct(s, h);
if (u < 0.0f || u > 1.0f) return false;
Vec3 q = CrossProduct(s, edge1);
float v = f * DotProduct(dir, q);
if (v < 0.0f || u + v > 1.0f) return false;
float t = f * DotProduct(edge2, q);
if (t > EPSILON)
{
out = l1 + dir * t;
return true;
}
return false;
}
void Translate(Transform& trans, Vec3 offset)
{
trans.Position += Vec3{offset.x, offset.y, offset.z};
}
void TranslateLocal(Transform& trans, Vec3 offset)
{
Vec3 localOffset = GlobalToLocalDirection(trans, offset);
trans.Position += Vec3{localOffset.x, localOffset.y, localOffset.z};
}
void Rotate(Transform& trans, Vec3 rotation)
{
float rot[16]{0};
bx::mtxRotateXYZ(rot, rotation.x, rotation.y, rotation.z);
float temp[16]{0};
bx::mtxMul(temp, rot, trans.Rotation.M);
bx::memCopy(trans.Rotation.M, temp, sizeof(temp));
}
void RotateLocal(Transform& trans, Vec3 rotation)
{
float rot[16]{0};
bx::mtxRotateXYZ(rot, rotation.x, rotation.y, rotation.z);
float temp[16]{0};
bx::mtxMul(temp, trans.Rotation.M, rot);
bx::memCopy(trans.Rotation.M, temp, sizeof(temp));
}
Vec3 AxisRight(const Mat4& trans)
{
return {trans.M[0], trans.M[1], trans.M[2]};
}
Vec3 AxisUp(const Mat4& trans)
{
return {trans.M[4], trans.M[5], trans.M[6]};
}
Vec3 AxisForward(const Mat4& trans)
{
return {trans.M[8], trans.M[9], trans.M[10]};
}
void UpdateMatrix(Transform& trans)
{
Mat4 pos;
Mat4 scale;
bx::mtxTranslate(pos.M, trans.Position.x, trans.Position.y, trans.Position.z);
bx::mtxScale(scale.M, trans.Scale.x, trans.Scale.y, trans.Scale.z);
Mat4 temp;
bx::mtxMul(temp.M, scale.M, trans.Rotation.M);
bx::mtxMul(trans.M.M, temp.M, pos.M);
bx::mtxInverse(trans.MI.M, trans.M.M);
}
Vec3 GlobalToLocalDirection(Transform& trans, Vec3 global)
{
UpdateMatrix(trans);
float in[4]{global.x, global.y, global.z, 0.0f};
float out[4]{0.0f};
bx::vec4MulMtx(out, in, Transpose(trans.MI).M);
return {out[0], out[1], out[2]};
}
Vec3 GlobalToLocalPoint(Transform& trans, Vec3 global)
{
UpdateMatrix(trans);
float in[4]{global.x, global.y, global.z, 1.0f};
float out[4]{0.0f};
bx::vec4MulMtx(out, in, trans.MI.M);
return {out[0], out[1], out[2]};
}
Vec3 LocalToGlobalDirection(Transform& trans, Vec3 local)
{
UpdateMatrix(trans);
float in[4]{local.x, local.y, local.z, 0.0f};
float out[4]{0.0f};
bx::vec4MulMtx(out, in, Transpose(trans.M).M);
return {out[0], out[1], out[2]};
}
Vec3 LocalToGlobalPoint(Transform& trans, Vec3 local)
{
UpdateMatrix(trans);
float in[4]{local.x, local.y, local.z, 1.0f};
float out[4]{0.0f};
bx::vec4MulMtx(out, in, trans.M.M);
return {out[0], out[1], out[2]};
}
} // namespace Gen

98
src/game/Gen.h Normal file
View File

@@ -0,0 +1,98 @@
#pragma once
#include "../gen/Generated.h"
namespace Gen
{
bool IsValid(const AssetHandle& h);
bool IsValid(const ModelHandle& h);
bool IsValid(const TextureHandle& h);
bool operator==(const AssetHandle& lhs, const AssetHandle& rhs);
bool operator==(const ModelHandle& lhs, const ModelHandle& rhs);
PuzPos operator+=(PuzPos lhs, const PuzPos& rhs);
PuzPos operator+(PuzPos lhs, const PuzPos& rhs);
Vec4& operator+=(Vec4& lhs, const Vec4& rhs);
Vec4 operator+(const Vec4& lhs, const Vec4& rhs);
Vec4& operator+=(Vec4& lhs, float rhs);
Vec4 operator+(Vec4& lhs, float rhs);
Vec4& operator-=(Vec4& lhs, const Vec4& rhs);
Vec4 operator-(const Vec4& lhs, const Vec4& rhs);
Vec4& operator-=(Vec4& lhs, float rhs);
Vec4 operator-(const Vec4& lhs, float rhs);
Vec4& operator*=(Vec4& lhs, float rhs);
Vec4 operator*(const Vec4& lhs, float rhs);
Vec4& operator/=(Vec4& lhs, float rhs);
Vec4 operator/(const Vec4& lhs, float rhs);
Vec3& operator+=(Vec3& lhs, const Vec3& rhs);
Vec3 operator+(const Vec3& lhs, const Vec3& rhs);
Vec3& operator+=(Vec3& lhs, float rhs);
Vec3 operator+(Vec3& lhs, float rhs);
Vec3& operator-=(Vec3& lhs, const Vec3& rhs);
Vec3 operator-(const Vec3& lhs, const Vec3& rhs);
Vec3& operator-=(Vec3& lhs, float rhs);
Vec3 operator-(const Vec3& lhs, float rhs);
Vec3& operator*=(Vec3& lhs, float rhs);
Vec3 operator*(const Vec3& lhs, float rhs);
Vec3& operator/=(Vec3& lhs, float rhs);
Vec3 operator/(const Vec3& lhs, float rhs);
Vec2& operator+=(Vec2& lhs, const Vec2& rhs);
Vec2 operator+(const Vec2& lhs, const Vec2& rhs);
Vec2& operator+=(Vec2& lhs, float rhs);
Vec2 operator+(Vec2& lhs, float rhs);
Vec2& operator-=(Vec2& lhs, const Vec2& rhs);
Vec2 operator-(const Vec2& lhs, const Vec2& rhs);
Vec2& operator-=(Vec2& lhs, float rhs);
Vec2 operator-(const Vec2& lhs, float rhs);
Vec2& operator*=(Vec2& lhs, float rhs);
Vec2 operator*(const Vec2& lhs, float rhs);
Vec2& operator/=(Vec2& lhs, float rhs);
Vec2 operator/(const Vec2& lhs, float rhs);
float Magnitude(const Vec4& vec);
float Magnitude(const Vec3& vec);
float Magnitude(const Vec2& vec);
Vec4 Normalized(const Vec4& vec);
Vec3 Normalized(const Vec3& vec);
Vec2 Normalized(const Vec2& vec);
Mat4 Inverse(const Mat4& mat);
Mat4 Transpose(const Mat4& mat);
Vec4 Mul(const Mat4& mat, const Vec4& vec);
Vec3 EulerFromRotation(const Mat4& rotation);
Mat4 RotationFromEuler(const Vec3& euler);
Mat4 RotationFromQuaternion(const Vec4& quaternion);
float DotProduct(Vec3 a, Vec3 b);
Vec3 CrossProduct(Vec3 a, Vec3 b);
Vec3 CrossProductFromPlane(Vec3 a, Vec3 b, Vec3 c);
bool RayPlaneIntersect(Vec3 l1, Vec3 l2, Vec3 p1, Vec3 p2, Vec3 p3, Vec3& out);
bool RayTriangleIntersect(Vec3 l1, Vec3 l2, Vec3 p1, Vec3 p2, Vec3 p3, Vec3& out);
void Translate(Transform& trans, Vec3 offset);
void TranslateLocal(Transform& trans, Vec3 offset);
void Rotate(Transform& trans, Vec3 rotation);
void RotateLocal(Transform& trans, Vec3 rotation);
Vec3 LocalToGlobalPoint(Transform& trans, Vec3 local);
Vec3 LocalToGlobalDirection(Transform& trans, Vec3 local);
Vec3 GlobalToLocalPoint(Transform& trans, Vec3 global);
Vec3 GlobalToLocalDirection(Transform& trans, Vec3 global);
Vec3 AxisRight(const Mat4& mat);
Vec3 AxisUp(const Mat4& mat);
Vec3 AxisForward(const Mat4& mat);
void UpdateMatrix(Transform& trans);
} // namespace Gen

View File

@@ -1,87 +1,18 @@
#include "../engine/Shared.h"
#include "Global.h"
#include "Instance.h"
#include "bx/bx.h"
#include "bx/math.h"
#include <cassert>
#include <cstdint>
using namespace Gen;
namespace
{
SharedData* SharedInstance = nullptr;
Game::GameInstance* GameInst = nullptr;
} // namespace
void Transform::CreateTransform(float* out, bx::Vec3 pos, bx::Quaternion rot, bx::Vec3 scale)
{
if (out == nullptr) return;
float rMat[16]{0};
float tMat[16]{0};
float sMat[16]{0};
bx::mtxFromQuaternion(rMat, bx::Quaternion{rot.x, rot.y, rot.z, rot.w});
bx::mtxTranslate(tMat, pos.x, pos.y, pos.z);
bx::mtxScale(sMat, scale.x, scale.y, scale.z);
float buf[16]{0};
bx::mtxMul(buf, rMat, sMat);
bx::mtxMul(out, buf, tMat);
}
void Transform::Translate(Vec3 offset)
{
Position = bx::add(Position, {offset.x, offset.y, offset.z});
}
void Transform::TranslateLocal(Vec3 offset)
{
Vec3 localOffset = GlobalToLocalDirection(offset);
Position = bx::add(Position, {localOffset.x, localOffset.y, localOffset.z});
}
void Transform::Rotate(bx::Vec3 rotation)
{
float rot[16]{0};
bx::mtxRotateXYZ(rot, rotation.x, rotation.y, rotation.z);
float temp[16]{0};
bx::mtxMul(temp, rot, Rotation.M);
bx::memCopy(Rotation.M, temp, sizeof(temp));
}
void Transform::RotateLocal(bx::Vec3 rotation)
{
float rot[16]{0};
bx::mtxRotateXYZ(rot, rotation.x, rotation.y, rotation.z);
float temp[16]{0};
bx::mtxMul(temp, Rotation.M, rot);
bx::memCopy(Rotation.M, temp, sizeof(temp));
}
Vec3 Transform::Right() const
{
return {M.M[0], M.M[1], M.M[2]};
}
Vec3 Transform::Up() const
{
return {M.M[4], M.M[5], M.M[6]};
}
Vec3 Transform::Forward() const
{
return {M.M[8], M.M[9], M.M[10]};
}
void Transform::UpdateMatrix()
{
Mat4 pos;
Mat4 scale;
bx::mtxTranslate(pos.M, Position.x, Position.y, Position.z);
bx::mtxScale(scale.M, Scale.x, Scale.y, Scale.z);
Mat4 temp;
bx::mtxMul(temp.M, scale.M, Rotation.M);
bx::mtxMul(M.M, pos.M, temp.M);
bx::mtxInverse(MI.M, M.M);
}
namespace Game
{
SharedData& GetShared()
@@ -106,69 +37,36 @@ namespace Game
GameInst = &instance;
}
void* AllocateScratch(size_t byteCount, size_t align)
uint8_t* AllocateScratch(uint64_t byteCount, uint32_t align)
{
size_t offset = GetInstance().UsedScratchAmount;
uint8_t* base = static_cast<uint8_t*>(GetShared().Game.TransientStorage);
uint8_t* current = base + offset;
size_t offsetAligned = ((offset + align - 1) / align) * align;
uint8_t* ptrAligned = base + offsetAligned;
size_t newOffset = offsetAligned + byteCount;
if (newOffset > GetShared().Game.TransientStorageSize) return nullptr;
GetInstance().UsedScratchAmount = newOffset;
return reinterpret_cast<void*>(ptrAligned);
assert(align <= 64); // The alignment of the arena limits the alignment that can be specified here!
auto& arena = GetShared().Game.TransientArena;
uint64_t offsetAligned = ((arena.Used + align - 1) / align) * align;
uint8_t* ptrAligned = arena.Base + offsetAligned;
uint64_t newOffset = offsetAligned + byteCount;
if (newOffset > arena.MaxSize) return nullptr;
arena.Used = newOffset;
arena.LastAllocSize = byteCount;
return ptrAligned;
}
bool ResizeLastScratchAlloc(uint64_t newByteCount)
{
auto& arena = GetShared().Game.TransientArena;
if (newByteCount > arena.LastAllocSize)
{
LOG_ERROR("Can't resize to more than previous size!");
return false;
}
arena.Used -= arena.LastAllocSize;
arena.Used += newByteCount;
return true;
}
void ResetScratch()
{
auto& arena = GetShared().Game.TransientArena;
arena.Used = 0;
arena.LastAllocSize = 0;
}
} // namespace Game
Vec3 Transform::GlobalToLocalDirection(Vec3 global)
{
UpdateMatrix();
float in[4]{global.x, global.y, global.z, 0.0f};
float out[4]{0.0f};
bx::vec4MulMtx(out, in, MI.M);
return {out[0], out[1], out[2]};
}
bx::Vec3 Transform::GlobalToLocalPoint(bx::Vec3 global)
{
UpdateMatrix();
float in[4]{global.x, global.y, global.z, 1.0f};
float out[4]{0.0f};
bx::vec4MulMtx(out, in, MI.M);
return {out[0], out[1], out[2]};
}
Vec3 Transform::LocalToGlobalDirection(Vec3 local)
{
UpdateMatrix();
float in[4]{local.x, local.y, local.z, 0.0f};
float out[4]{0.0f};
Mat4 test = M.Transpose();
bx::vec4MulMtx(out, in, test.M);
return {out[0], out[1], out[2]};
}
bx::Vec3 Transform::LocalToGlobalPoint(bx::Vec3 local)
{
UpdateMatrix();
float in[4]{local.x, local.y, local.z, 1.0f};
float out[4]{0.0f};
bx::vec4MulMtx(out, in, M.M);
return {out[0], out[1], out[2]};
}
void Transform::SetPosition(Vec3 pos)
{
Position = {pos.x, pos.y, pos.z};
}
Vec3 Transform::GetPosition()
{
return {Position.x, Position.y, Position.z};
}
Mat4 Mat4::Inverse()
{
Mat4 result;
bx::mtxInverse(result.M, M);
return result;
}
Mat4 Mat4::Transpose()
{
Mat4 result;
bx::mtxTranspose(result.M, M);
return result;
}

View File

@@ -1,186 +1,6 @@
#pragma once
#include "bx/math.h"
#include <cfloat>
struct Vec2
{
float x = 0.0f;
float y = 0.0f;
Vec2& operator+=(const Vec2& rhs)
{
x += rhs.x;
y += rhs.y;
return *this;
}
friend Vec2 operator+(Vec2 lhs, const Vec2& rhs)
{
lhs += rhs;
return lhs;
}
Vec2& operator-=(const Vec2& rhs)
{
x -= rhs.x;
y -= rhs.y;
return *this;
}
friend Vec2 operator-(Vec2 lhs, const Vec2& rhs)
{
lhs -= rhs;
return lhs;
}
Vec2& operator*=(const float rhs)
{
x *= rhs;
y *= rhs;
return *this;
}
friend Vec2 operator*(Vec2 lhs, const float rhs)
{
lhs *= rhs;
return lhs;
}
Vec2& operator/=(const float rhs)
{
x /= rhs;
y /= rhs;
return *this;
}
friend Vec2 operator/(Vec2 lhs, const float rhs)
{
lhs /= rhs;
return lhs;
}
float Magnitude()
{
return bx::sqrt(x * x + y * y);
}
Vec2 Normalize()
{
float mag = Magnitude();
if (mag < FLT_EPSILON) return {};
return {x / mag, y / mag};
}
};
struct Vec3
{
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
Vec3& operator+=(const Vec3& rhs)
{
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
friend Vec3 operator+(Vec3 lhs, const Vec3& rhs)
{
lhs += rhs;
return lhs;
}
Vec3& operator-=(const Vec3& rhs)
{
x -= rhs.x;
y -= rhs.y;
z -= rhs.z;
return *this;
}
friend Vec3 operator-(Vec3 lhs, const Vec3& rhs)
{
lhs -= rhs;
return lhs;
}
Vec3& operator*=(const float rhs)
{
x *= rhs;
y *= rhs;
z *= rhs;
return *this;
}
friend Vec3 operator*(Vec3 lhs, const float rhs)
{
lhs *= rhs;
return lhs;
}
Vec3& operator/=(const float rhs)
{
x /= rhs;
y /= rhs;
z /= rhs;
return *this;
}
friend Vec3 operator/(Vec3 lhs, const float rhs)
{
lhs /= rhs;
return lhs;
}
float Magnitude()
{
return bx::sqrt(x * x + y * y + z * z);
}
Vec3 Normalize()
{
float mag = Magnitude();
if (mag < FLT_EPSILON) return {};
return {x / mag, y / mag, z / mag};
}
};
struct Vec4
{
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float w = 0.0f;
};
struct Mat3
{
// clang-format off
float M[9]{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
};
// clang-format on
};
struct Mat4
{
// clang-format off
float M[16]{
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
};
// clang-format on
Mat4 Inverse();
Mat4 Transpose();
};
#include <cstdint>
inline int32_t SetFlags(int32_t in, int32_t flags)
{
@@ -203,32 +23,6 @@ inline bool GetFlag(int32_t in, int32_t flags)
return (in & flags) > 0;
}
struct Transform
{
Mat4 M;
Mat4 MI;
bx::Vec3 Position{0.0f, 0.0f, 0.0f};
Mat4 Rotation;
bx::Vec3 Scale{1.0f, 1.0f, 1.0f};
static void CreateTransform(float* out, bx::Vec3 pos, bx::Quaternion rot, bx::Vec3 scale);
void Translate(Vec3 offset);
void TranslateLocal(Vec3 offset);
void Rotate(bx::Vec3 rotation);
void RotateLocal(bx::Vec3 rotation);
bx::Vec3 LocalToGlobalPoint(bx::Vec3 local);
Vec3 LocalToGlobalDirection(Vec3 local);
bx::Vec3 GlobalToLocalPoint(bx::Vec3 global);
Vec3 GlobalToLocalDirection(Vec3 global);
Vec3 Right() const;
Vec3 Up() const;
Vec3 Forward() const;
void SetPosition(Vec3 pos);
Vec3 GetPosition();
const float* GetPtr();
void UpdateMatrix();
};
struct SharedData;
namespace Game
@@ -239,5 +33,7 @@ namespace Game
void SetShared(SharedData& instance);
GameInstance& GetInstance();
void SetInstance(GameInstance& instance);
void* AllocateScratch(size_t byteCount, size_t align = 16);
uint8_t* AllocateScratch(uint64_t byteCount, uint32_t align = 16);
bool ResizeLastScratchAlloc(uint64_t newByteCount);
void ResetScratch();
} // namespace Game

View File

@@ -2,17 +2,22 @@
#include "Global.h"
#include "Input.h"
#include "imgui.h"
#include "imgui_internal.h"
namespace Game
{
using namespace Gen;
bool IsKeyboardAllowed()
{
if (GImGui == nullptr) return true;
auto& IO = ImGui::GetIO();
return !IO.WantCaptureKeyboard || GetFlag(IO.ConfigFlags, ImGuiConfigFlags_NoKeyboard);
}
bool IsMouseAllowed()
{
if (GImGui == nullptr) return true;
auto& IO = ImGui::GetIO();
return !IO.WantCaptureMouse || GetFlag(IO.ConfigFlags, ImGuiConfigFlags_NoMouse);
}
@@ -56,4 +61,11 @@ namespace Game
if (!IsMouseAllowed()) return {};
return {GetShared().Window.MouseDeltaX, GetShared().Window.MouseDeltaY};
}
Vec2 GetMousePos()
{
// TODO: fix this!!
if (GImGui == nullptr) return {};
ImVec2 pos = ImGui::GetMousePos();
return {pos.x, pos.y};
}
} // namespace Game

View File

@@ -1,5 +1,5 @@
#pragma once
#include "Global.h"
#include "../gen/Generated.h"
enum class MouseButton
{
@@ -393,5 +393,6 @@ namespace Game
bool GetMouseButton(MouseButton button);
bool GetMouseButtonPressedNow(MouseButton button);
bool GetMouseButtonReleasedNow(MouseButton button);
Vec2 GetMouseMovement();
Gen::Vec2 GetMouseMovement();
Gen::Vec2 GetMousePos();
} // namespace Game

View File

@@ -17,6 +17,12 @@ namespace Game
Game,
};
enum class InteractionMode
{
Walk,
ReadTablet,
};
struct Time
{
double Now = 0.0;
@@ -28,34 +34,43 @@ namespace Game
struct PlayerData
{
Transform FreeflyCamTransform;
Gen::Transform PlayerCamTransform;
Gen::Transform FreeflyCamTransform;
Gen::Mat4 Projection;
Gen::Mat4 ProjectionInverse;
float FreeflyXRot = 0.0f;
float FreeflyYRot = 0.0f;
float WalkXRot = 0.0f;
float WalkYRot = 0.0f;
Transform PlayerCamTransform;
CameraMode CameraM = CameraMode::Freefly;
CameraMode CameraM = CameraMode::Walk;
InputMode InputM = InputMode::Game;
InteractionMode InteractionM = InteractionMode::Walk;
float MouseSensitivity = 1.0f;
float MovementSpeed = 10.0f;
Gen::SavedPlayerConfig Config;
};
struct InstanceDebugData
{
uint16_t SelectedDebugLevel = UINT16_MAX;
static constexpr uint32_t MaxAssets = 128;
uint16_t SelectedDebugLevel = 0;
uint64_t ImguiIniSize = 0;
char ImguiIni[4096]{0};
static constexpr uint32_t MaxAssets = 128;
uint32_t AssetCount = 0;
uint32_t AssetHandles[MaxAssets]{0};
Gen::AssetHandle AssetHandles[MaxAssets]{0};
char AssetHandlePaths[MaxAssets][128];
bool ShowImguiDemo = false;
bool DebugBreakIDEnabled = false;
int DebugBreakID = -1;
uint8_t DebugCardRotation = 0;
bool ShortenLogFileNames = true;
bool ShowStats = true;
};
struct GameInstance
{
bool IsInitialized = false;
uint64_t Size = sizeof(GameInstance);
uint64_t UsedScratchAmount = 0;
Time Time;
PlayerData Player;
Level GameLevel;

View File

@@ -1,9 +1,16 @@
#include "../gen/Def.h"
#include "Gen.h"
#include "Global.h"
#include "Input.h"
#include "Instance.h"
#include "Level.h"
#include "Log.h"
#include "Puzzle.h"
#include "UI.h"
#include "bx/bx.h"
#include "bx/debug.h"
#include "rendering/Rendering.h"
#include "SDL3/SDL_mouse.h"
#include "bgfx/bgfx.h"
#include "bx/error.h"
@@ -11,25 +18,38 @@
#include "bx/filepath.h"
#include "bx/string.h"
#include "imgui.h"
#include "rendering/Rendering.h"
#include <SDL3/SDL.h>
#include <bx/math.h>
#include <cstdint>
#include <tracy/tracy.hpp>
#include <tracy/Tracy.hpp>
using namespace Gen;
namespace Game
{
void EntityRenderData::Render(const Model* models, const Material* materials)
void EntityRenderData::Render(const Model* models, const Material* materials, const Texture* textures)
{
if (ModelHandle == UINT16_MAX || MaterialHandle == UINT16_MAX) return;
auto& debug = GetInstance().DebugData;
if ((int32_t)debug.DebugBreakID == RenderID && debug.DebugBreakIDEnabled)
{
bx::debugBreak();
debug.DebugBreakIDEnabled = false;
}
if (models == nullptr || materials == nullptr || textures == nullptr) return;
if (!Gen::IsValid(ModelH) || MaterialHandle >= EMaterial::EntryCount) return;
if (!Visible) return;
auto& rendering = GameRendering::Get();
Transform.UpdateMatrix();
UpdateMatrix(Transform);
bgfx::setTransform(Transform.M.M);
const Model& currentModel = models[ModelHandle];
const Material& currentMaterial = materials[MaterialHandle];
const Model& currentModel = models[ModelH.ModelIdx];
const Material& currentMaterial = materials[(uint16_t)MaterialHandle];
if (!isValid(currentModel.IndexBuffer) || !isValid(currentModel.VertexBuffer)) return;
if (!isValid(currentMaterial.Shader)) return;
bgfx::setVertexBuffer(0, currentModel.VertexBuffer);
bgfx::setIndexBuffer(currentModel.IndexBuffer);
bgfx::setState(currentMaterial.State);
@@ -38,45 +58,78 @@ namespace Game
timeValues[0] = GetInstance().Time.Now;
float texInfo[4]{0.0f};
texInfo[0] = currentMaterial.Textures[0].Info.width;
texInfo[1] = currentMaterial.Textures[0].Info.height;
texInfo[0] = textures[0].Info.width;
texInfo[1] = textures[0].Info.height;
texInfo[2] = rendering.DitherTextures.DitherTexWH;
texInfo[3] = rendering.DitherTextures.DitherTexDepth;
bgfx::setTexture(0, currentMaterial.Textures[0].SamplerHandle, currentMaterial.Textures[0].Handle);
bgfx::UniformHandle sampler = rendering.DefaultSampler;
bgfx::TextureHandle tex = rendering.Textures[0].RenderHandle;
if (IsValid(TextureHandle))
{
sampler = textures[TextureHandle.TextureIdx].SamplerHandle;
tex = textures[TextureHandle.TextureIdx].RenderHandle;
texInfo[0] = textures[TextureHandle.TextureIdx].Info.width;
texInfo[1] = textures[TextureHandle.TextureIdx].Info.height;
}
bgfx::setTexture(0, sampler, tex);
bgfx::setTexture(1, rendering.DitherTextures.DitherSampler, rendering.DitherTextures.FinalTex);
bgfx::setTexture(2, rendering.DitherTextures.RampSampler, rendering.DitherTextures.RampTex);
bgfx::setUniform(currentMaterial.Uniforms[Material::UTime], timeValues);
bgfx::setUniform(currentMaterial.Uniforms[Material::UDotColor], &TestColor.x);
bgfx::setUniform(currentMaterial.Uniforms[Material::UDotColor], &DotColor.x);
bgfx::setUniform(currentMaterial.Uniforms[Material::UTexInfo], texInfo);
bgfx::setUniform(currentMaterial.Uniforms[Material::UBaseColor], &BaseColor.x);
bgfx::submit(currentMaterial.ViewID, currentMaterial.Shader);
}
void EntityRenderData::LoadFromSaved(const Gen::SavedEntityRenderData& saved)
{
DotColor = saved.HighlightColor;
BaseColor = saved.BaseColor;
Transform = saved.TF;
// TODO: fix handle indices
MaterialHandle = saved.Material;
TextureHandle = saved.Texture;
ModelH = saved.Model;
Visible = saved.Visible;
}
namespace
{
void UpdatePlayerInputMode()
{
bool IsGaming = GetInstance().Player.InputM == InputMode::Game;
SDL_SetWindowRelativeMouseMode(GetShared().Window.SDLWindow, IsGaming);
bool captureMouse = IsGaming && GetInstance().Player.InteractionM == InteractionMode::Walk;
SDL_SetWindowRelativeMouseMode(GetShared().Window.SDLWindow, captureMouse);
auto& rendering = GameRendering::Get();
if (rendering.SetupData.UseImgui)
{
auto& IO = ImGui::GetIO();
IO.ConfigFlags = FlagBool(IO.ConfigFlags, ImGuiConfigFlags_NoMouse | ImGuiConfigFlags_NoKeyboard, IsGaming);
GameRendering::Get().UIVisible = IsGaming ? UIVisibilityState::Game : UIVisibilityState::Debug;
IO.ConfigFlags =
FlagBool(IO.ConfigFlags, ImGuiConfigFlags_NoMouse | ImGuiConfigFlags_NoKeyboard, captureMouse);
}
rendering.UIVisible = IsGaming ? UIVisibilityState::Game : UIVisibilityState::Debug;
}
} // namespace
void Level::Setup(GameData& data)
{
LOG("Level setup");
void* storagePtr = data.EntityStorage;
uint8_t* storagePtr = data.EntityArena.Base;
bool needReset = false;
needReset |= Cubes.Setup(storagePtr, needReset);
needReset |= Tests.Setup(storagePtr, needReset);
needReset |= PuzzleTiles.Setup(storagePtr, needReset);
needReset |= PuzzleTileCovers.Setup(storagePtr, needReset);
needReset |= UIQuads.Setup(storagePtr, needReset);
needReset |= LevelEntities.Setup(storagePtr, needReset);
Puzzle::Setup();
UIQuads.Count = 0;
PuzzleTiles.Count = 0;
bx::Error err;
bx::DirectoryReader dirIter;
@@ -98,10 +151,9 @@ namespace Game
fullPath.join(info.filePath);
LOG("Loading %s", fullPath.getCPtr());
Generated::Deserializer ser;
ser.Init(fullPath);
Generated::PuzzleData dataBuf;
if (ser.ReadT("PZZL", dataBuf))
Gen::Deserializer ser;
Gen::PuzzleData dataBuf;
if (ser.Init(fullPath, "PZZL") && ser.ReadT(dataBuf))
{
if (dataBuf.ID >= BX_COUNTOF(Puzzles))
{
@@ -130,33 +182,113 @@ namespace Game
LOG_ERROR("Failed to open puzzle dir at %s:\n%s", puzzleDirPath.getCPtr(), err.getMessage().getCPtr());
}
if (Cubes.Count == 0)
if (!IsValid(PlayerOutsideViewCube))
{
for (uint32_t yy = 0; yy < 11; ++yy)
{
for (uint32_t xx = 0; xx < 11; ++xx)
{
Cube& c = Cubes.Get(Cubes.New());
c.TestX = xx;
c.TestY = yy;
c.Setup();
PlayerOutsideViewCube = Cubes.New();
Cubes.Get(PlayerOutsideViewCube).Setup();
}
}
Cubes.New(); // Floor
}
if (Tests.Count == 0)
{
Tests.Get(Tests.New()).Setup();
Deserializer d;
d.Init("game/data/static/uiconfig.dat", "UICO");
d.ReadT(GetInstance().Player.Config);
d.Finish();
}
if (PuzzleTiles.Count == 0)
UIQuads.Count = 0;
PuzzleTiles.Count = 0;
PuzzleTileCovers.Count = 0;
for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i)
{
for (uint32_t puzI = 0; puzI < BX_COUNTOF(Puzzles); ++puzI)
if (Puzzles[i].Data.ID != UINT16_MAX)
{
Puzzles[puzI].Setup();
Puzzles[i].Setup();
}
}
PuzzleUI.Setup();
ReloadLevelEntities();
UpdatePlayerInputMode();
for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i)
{
Puzzles[i].WorldPosition = {0.0f, 0.0f, i * 50.0f};
}
}
bool IsInPuzzle(WorldPuzzle& puz, Vec3 worldPos)
{
Vec3 offsetToPuzzle = worldPos - puz.WorldPosition + (Vec3{0.5f, 0.0f, 0.5f} * Puzzle::Config::CardScaleWorld);
Vec3 scaledOffset = offsetToPuzzle / Puzzle::Config::CardScaleWorld;
int32_t offsetX = (int32_t)bx::floor(scaledOffset.x);
int32_t offsetY = (int32_t)bx::floor(scaledOffset.z);
return (offsetX >= 0 && offsetX < puz.Data.WidthTiles / 2 && offsetY >= -1 &&
offsetY < puz.Data.HeightTiles / 2);
}
bool IsOnGround(Level& level, Vec3 worldPos)
{
for (auto& puz : level.Puzzles)
{
Vec3 offsetToPuzzle =
worldPos - puz.WorldPosition + (Vec3{0.5f, 0.0f, 0.5f} * Puzzle::Config::CardScaleWorld);
Vec3 scaledOffset = offsetToPuzzle / Puzzle::Config::CardScaleWorld;
int32_t offsetX = (int32_t)bx::floor(scaledOffset.x);
int32_t offsetY = puz.Data.HeightTiles / 2 - (int32_t)bx::floor(scaledOffset.z) - 1;
float fracOffsetX = scaledOffset.x - offsetX;
float fracOffsetY = scaledOffset.z - bx::floor(scaledOffset.z);
if (offsetX >= 0 && offsetX < puz.Data.WidthTiles / 2 && offsetY >= 0 && offsetY < puz.Data.HeightTiles / 2)
{
auto& card = puz.Data.PlacedCards[offsetY * Puzzle::Config::MaxPuzzleSizeCards + offsetX];
if (card.RefCard.Idx == UINT16_MAX)
{
return true;
}
auto& refCard = Puzzle::GetStaticPuzzleData().Cards[card.RefCard.Idx];
if (!IsValid(refCard.BaseModelHandle))
{
LOG_WARN("missing base model! @ %i %i", offsetX, offsetY);
return true;
}
auto& heightmap = GameRendering::Get().Models[refCard.BaseModelHandle.ModelIdx].Height;
int32_t xPos = (int32_t)(fracOffsetX * heightmap.Width);
int32_t yPos = (int32_t)(fracOffsetY * heightmap.Height);
uint8_t height = 0;
switch (card.Rotation)
{
case 0:
height = heightmap.Values[yPos * heightmap.Width + xPos];
break;
case 1:
height = heightmap.Values[xPos * heightmap.Width + (heightmap.Height - yPos - 1)];
break;
case 2:
height =
heightmap
.Values[(heightmap.Height - yPos - 1) * heightmap.Width + (heightmap.Width - xPos - 1)];
break;
default:
height = heightmap.Values[(heightmap.Height - xPos - 1) * heightmap.Width + yPos];
break;
}
return height >= 110 && height <= 125;
}
if (offsetX == 1 && offsetY == puz.Data.HeightTiles / Puzzle::Config::CardSize)
{
if (puz.IsSolved)
{
return true;
}
return true; // TODO!
}
}
return false;
}
void Level::Update()
@@ -167,9 +299,8 @@ namespace Game
// Input
float delta = GetInstance().Time.Delta;
delta = 1.0f / 144.0f;
constexpr float moveSpeed = 10.0f;
constexpr float rotSpeed = 0.6f;
float moveSpeed = player.MovementSpeed;
float rotSpeed = player.MouseSensitivity;
float forwardInput = (GetKey(ScanCode::W) ? 1.0f : 0.0f) + (GetKey(ScanCode::S) ? -1.0f : 0.0f);
float rightInput = (GetKey(ScanCode::D) ? 1.0f : 0.0f) + (GetKey(ScanCode::A) ? -1.0f : 0.0f);
@@ -177,7 +308,7 @@ namespace Game
moveInput = bx::normalize(moveInput);
bx::Vec3 inputVec = {moveInput.x * delta * moveSpeed, 0.0f, moveInput.y * delta * moveSpeed};
Vec2 mouseMovement = GetMouseMovement();
bx::Vec3 rotInput = {mouseMovement.y * delta * rotSpeed, mouseMovement.x * delta * rotSpeed, 0.0f};
bx::Vec3 rotInput = {mouseMovement.y * delta * -rotSpeed, mouseMovement.x * delta * -rotSpeed, 0.0f};
if (GetKeyPressedNow(ScanCode::F1))
{
@@ -203,23 +334,57 @@ namespace Game
{
player.FreeflyXRot += rotInput.x;
player.FreeflyYRot += rotInput.y;
bx::mtxRotateY(player.FreeflyCamTransform.Rotation.M, player.FreeflyYRot);
player.FreeflyCamTransform.RotateLocal({player.FreeflyXRot, 0.0f, 0.0f});
bx::mtxRotateXYZ(player.FreeflyCamTransform.Rotation.M, player.FreeflyXRot, player.FreeflyYRot, 0.0f);
}
player.FreeflyCamTransform.TranslateLocal({0.0f, 0.0f, -inputVec.z});
player.FreeflyCamTransform.TranslateLocal({-inputVec.x, 0.0f, 0.0f});
TranslateLocal(player.FreeflyCamTransform, {0.0f, 0.0f, inputVec.z});
TranslateLocal(player.FreeflyCamTransform, {inputVec.x, 0.0f, 0.0f});
}
else if (player.CameraM == CameraMode::Walk)
{
player.PlayerCamTransform.TranslateLocal({0.0f, 0.0f, -inputVec.z});
player.PlayerCamTransform.TranslateLocal({-inputVec.x, 0.0f, 0.0f});
player.PlayerCamTransform.Position.y = -3.0f;
auto newTransform = player.PlayerCamTransform;
// Global and local are inverted because camera
Vec3 globalInput = GlobalToLocalDirection(newTransform, {inputVec.x, 0.0f, inputVec.z});
Translate(newTransform, globalInput);
newTransform.Position.y = 3.0f;
if (IsOnGround(*this, newTransform.Position))
{
player.PlayerCamTransform = newTransform;
}
else
{
auto newTransform = player.PlayerCamTransform;
Translate(newTransform, {globalInput.x, 0.0f, 0.0f});
newTransform.Position.y = 3.0f;
if (IsOnGround(*this, newTransform.Position))
{
player.PlayerCamTransform = newTransform;
}
else
{
auto newTransform = player.PlayerCamTransform;
Translate(newTransform, {0.0f, 0.0f, globalInput.z});
newTransform.Position.y = 3.0f;
if (IsOnGround(*this, newTransform.Position))
{
player.PlayerCamTransform = newTransform;
}
}
}
player.WalkXRot += rotInput.x;
player.WalkYRot += rotInput.y;
bx::mtxRotateY(player.PlayerCamTransform.Rotation.M, player.WalkYRot);
player.PlayerCamTransform.RotateLocal({player.WalkXRot, 0.0f, 0.0f});
bx::mtxRotateXYZ(player.PlayerCamTransform.Rotation.M, player.WalkXRot, player.WalkYRot, 0.0f);
}
if (GetKeyPressedNow(ScanCode::SPACE))
{
player.InteractionM = player.InteractionM == InteractionMode::ReadTablet ? InteractionMode::Walk
: InteractionMode::ReadTablet;
UpdatePlayerInputMode();
}
// Cubes
@@ -229,125 +394,235 @@ namespace Game
}
// Puzzle tiles
Puzzle::PuzzleSolver solver;
uint16_t activeIdx = GetInstance().DebugData.SelectedDebugLevel;
for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i)
{
Puzzles[i].Update();
if (IsInPuzzle(Puzzles[i], player.PlayerCamTransform.Position))
{
activeIdx = i;
}
Puzzles[i].IsActive = activeIdx == i;
Puzzles[i].Update();
Puzzles[i].IsSolved = solver.IsPuzzleSolved(Puzzles[i].Data);
}
PuzzleUI.Update(Puzzles[activeIdx].Data, Puzzles[activeIdx].IsSolved);
END_PERF(GetShared().Window.PerfCounters, PerfCounterType::GameLevelUpdate, GetShared().Window.FrameCounter);
}
void Level::Render(uint16_t viewId, const Model* models, const Material* materials)
void Level::Render(uint16_t viewId, const Model* models, const Material* materials, const Texture* textures)
{
ZoneScopedN("Level Render");
auto& shared = GetShared();
auto& player = GetInstance().Player;
float proj[16];
bx::mtxProj(proj,
bx::mtxProj(&player.Projection.M[0],
75.0f,
float(shared.Window.WindowWidth) / float(shared.Window.WindowHeight),
0.1f,
1000.0f,
bgfx::getCaps()->homogeneousDepth);
bx::mtxInverse(&player.ProjectionInverse.M[0], &player.Projection.M[0]);
auto& player = GetInstance().Player;
if (player.CameraM == CameraMode::Freefly)
bool isFreefly = player.CameraM == CameraMode::Freefly;
Cubes.Get(PlayerOutsideViewCube).EData.Visible = isFreefly;
if (isFreefly)
{
player.FreeflyCamTransform.UpdateMatrix();
bgfx::setViewTransform(viewId, player.FreeflyCamTransform.M.M, proj);
UpdateMatrix(player.FreeflyCamTransform);
bgfx::setViewTransform(viewId, player.FreeflyCamTransform.MI.M, &player.Projection.M[0]);
}
else
{
player.PlayerCamTransform.UpdateMatrix();
bgfx::setViewTransform(viewId, player.PlayerCamTransform.M.M, proj);
UpdateMatrix(player.PlayerCamTransform);
bgfx::setViewTransform(viewId, player.PlayerCamTransform.MI.M, &player.Projection.M[0]);
}
Cubes.Render(models, materials);
Tests.Render(models, materials);
PuzzleTiles.Render(models, materials);
UIQuads.Render(models, materials);
bgfx::touch(viewId);
Cubes.Render(models, materials, textures);
Tests.Render(models, materials, textures);
PuzzleTiles.Render(models, materials, textures);
PuzzleTileCovers.Render(models, materials, textures);
if (player.InteractionM == InteractionMode::ReadTablet)
{
UIQuads.Render(models, materials, textures);
}
LevelEntities.Render(models, materials, textures);
}
void Cube::Setup()
{
EData.MaterialHandle = 0;
EData.ModelHandle = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf");
EData.MaterialHandle = EMaterial::UI;
EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf");
}
void Cube::Update()
{
if (TestX >= 0 && TestY >= 0)
{
double globalTime = GetInstance().Time.Now;
double time = TestY <= 5 ? globalTime * 1.0f : 0.0f;
float scale = 1.0f + TestX * 0.4f;
EData.Transform.Position = bx::Vec3{TestX * 2.0f, TestY * 2.0f, 0.0f};
EData.Transform.Scale = {scale, scale, scale};
}
else
{
EData.Transform.Position = {0.0f, -1.0f, 0.0f};
EData.Transform.Scale = {100.0f, 1.0f, 100.0f};
}
EData.Transform.Position = GetInstance().Player.PlayerCamTransform.Position;
EData.Transform.Rotation = GetInstance().Player.PlayerCamTransform.Rotation;
EData.Transform.Scale = {0.2f, 0.2f, 0.2f};
}
void TestEntity::Setup()
{
EData.MaterialHandle = 0;
EData.ModelHandle = GameRendering::Get().GetModelHandleFromPath("models/zurg.gltf");
EData.Transform.Position = {0.0f, 0.0f, 0.0f};
EData.MaterialHandle = EMaterial::Default;
EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf");
}
void WorldPuzzle::Setup()
{
Data.PlacedCardCount = 16;
for (int32_t i = 0; i < 16; ++i)
{
Data.PlacedCards[i].RefCard = {0};
Data.PlacedCards[i].Position = {int8_t(i % 4), int8_t(i / 4)};
}
for (uint32_t cardI = 0; cardI < Data.PlacedCardCount; ++cardI)
{
const Generated::PlacedPuzzleCard& card = Data.PlacedCards[cardI];
Level& level = GetInstance().GameLevel;
TileHandles[cardI] = level.PuzzleTiles.New();
UIPlacedCards[cardI] = level.UIQuads.New();
for (int32_t i = 0; i < Puzzle::Config::MaxCardsInPuzzle; ++i)
{
TileHandles[i] = level.PuzzleTiles.New();
PuzzleTileEntity& tile = level.PuzzleTiles.Get(TileHandles[i]);
tile.EData.MaterialHandle = EMaterial::Default;
bx::Vec3 Pos = {
WorldPosition.x + card.Position.X * WorldCardSize.x,
WorldPosition.y,
WorldPosition.z + card.Position.Y * WorldCardSize.y,
};
PuzzleTileEntity& tile = level.PuzzleTiles.Get(TileHandles[cardI]);
tile.EData.Transform.Position = Pos;
tile.EData.MaterialHandle = 0;
UIQuadEntity& quad = level.UIQuads.Get(UIPlacedCards[cardI]);
quad.EData.MaterialHandle = 0;
quad.EData.ModelHandle = GameRendering::Get().GetModelHandleFromPath("models/plane.glb");
for (int32_t j = 0; j < Puzzle::Config::MaxCoversInTile; ++j)
{
int32_t idx = i * Puzzle::Config::MaxCoversInTile + j;
CoverHandles[idx] = level.PuzzleTileCovers.New();
PuzzleTileCover& cover = level.PuzzleTileCovers.Get(CoverHandles[idx]);
cover.EData.Visible = false;
}
}
for (int32_t i = 0; i < BX_COUNTOF(EndHandles); ++i)
{
EndHandles[i] = level.PuzzleTiles.New();
PuzzleTileEntity& tile = level.PuzzleTiles.Get(EndHandles[i]);
tile.EData.MaterialHandle = EMaterial::Default;
}
WallHandle = level.PuzzleTiles.New();
PuzzleTileEntity& wHandle = level.PuzzleTiles.Get(WallHandle);
wHandle.EData.MaterialHandle = EMaterial::Default;
wHandle.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/GateWall.glb");
DoorHandle = level.PuzzleTiles.New();
PuzzleTileEntity& dHandle = level.PuzzleTiles.Get(DoorHandle);
dHandle.EData.MaterialHandle = EMaterial::Default;
dHandle.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/GateDoor.glb");
IsSetup = true;
LOG("finished setup!");
}
Vec3 PuzPosToWorldPos(const PuzzleData& data, int32_t x, int32_t y)
{
return {
(float)x * Puzzle::Config::CardScaleWorld,
-5.0f,
(float)(data.HeightTiles / 2 - y - 1) * Puzzle::Config::CardScaleWorld,
};
}
void WorldPuzzle::Update()
{
Transform& camTransform = GetInstance().Player.PlayerCamTransform;
Vec3 cameraPos = camTransform.GetPosition() * -1;
Level& level = GetInstance().GameLevel;
auto& staticCards = Puzzle::GetStaticPuzzleData().Cards;
auto& visuals = Puzzle::GetStaticPuzzleData().Visuals;
for (int32_t cardI = 0; cardI < Data.PlacedCardCount; ++cardI)
// Board
for (int8_t y = 0; y < Data.HeightTiles / Puzzle::Config::CardSize; ++y)
{
Generated::PlacedPuzzleCard& card = Data.PlacedCards[cardI];
if (!Puzzle::IsValid(card.RefCard)) continue;
const Generated::StaticPuzzleCard& cData = Puzzle::GetCard(card.RefCard);
level.PuzzleTiles.Get(TileHandles[cardI]).EData.ModelHandle = cData.ModelHandle;
for (int8_t x = 0; x < Data.WidthTiles / Puzzle::Config::CardSize; ++x)
{
int32_t cardIdx = y * Puzzle::Config::MaxPuzzleSizeCards + x;
Gen::PlacedPuzzleCard& card = Data.PlacedCards[cardIdx];
auto& tile = level.PuzzleTiles.Get(TileHandles[cardIdx]);
auto& quad = level.UIQuads.Get(UIPlacedCards[cardI]);
Vec3 fw = {0, -1, 0};
// quad.EData.Transform.SetPosition(cameraPos + Vec3{0, -2, 0});
quad.EData.Transform.SetPosition({});
quad.EData.Transform.Rotation = camTransform.Rotation;
bool isValid = Puzzle::IsValid(card.RefCard);
auto& staticCard = isValid ? staticCards[card.RefCard.Idx] : staticCards[0];
// World Tile
tile.EData.Visible = true;
tile.EData.ModelH = staticCard.BaseModelHandle;
tile.EData.TextureHandle = staticCard.ModelTextureHandle;
tile.EData.DotColor = visuals.TileDotColor;
tile.EData.BaseColor = visuals.TileBaseColor;
Vec3 cardPos = PuzPosToWorldPos(Data, card.Position.X, card.Position.Y);
if (!isValid)
{
cardPos = PuzPosToWorldPos(Data, x, y);
}
tile.EData.Transform.Position = cardPos + WorldPosition;
bx::mtxRotateY(tile.EData.Transform.Rotation.M, card.Rotation * bx::kPi * -0.5f);
// Covers
if (IsValid(staticCard.BaseModelHandle))
{
auto& model = GameRendering::Get().Models[staticCard.BaseModelHandle.ModelIdx];
for (int32_t i = 0; i < model.SocketCount; ++i)
{
auto& cover =
level.PuzzleTileCovers.Get(CoverHandles[cardIdx * Puzzle::Config::MaxCoversInTile + i]);
cover.EData.Visible = IsActive;
cover.EData.ModelH = staticCard.Sockets[i].Model;
cover.EData.Transform = tile.EData.Transform;
cover.EData.MaterialHandle = EMaterial::Default;
cover.EData.BaseColor = {0.2f, 0.1f, 0.7f, 1.0f};
cover.EData.DotColor = {0.2f, 0.2f, 0.8f, 1.0f};
Gen::TranslateLocal(cover.EData.Transform, model.Sockets[i].Pos);
Gen::RotateLocal(cover.EData.Transform, Gen::EulerFromRotation(model.Sockets[i].Rot));
}
}
}
}
// End
for (int32_t i = 0; i < BX_COUNTOF(EndHandles); ++i)
{
auto& tile = level.PuzzleTiles.Get(EndHandles[i]);
if (i < Data.WidthTiles / 2)
{
tile.EData.Visible = true;
tile.EData.ModelH = staticCards[0].BaseModelHandle;
tile.EData.TextureHandle = staticCards[0].ModelTextureHandle;
tile.EData.DotColor = visuals.TileDotColor;
tile.EData.BaseColor = visuals.TileBaseColor + Vec4{0.1f, 0.1f, 0.1f, 0.0f};
tile.EData.Transform.Position = WorldPosition + Vec3{
i * Puzzle::Config::CardScaleWorld,
-5.0f,
(float)Data.HeightTiles / Puzzle::Config::CardSize *
Puzzle::Config::CardScaleWorld,
};
}
else
{
tile.EData.Visible = false;
}
}
auto& wall = level.PuzzleTiles.Get(WallHandle);
wall.EData.Visible = true;
wall.EData.Transform.Position =
WorldPosition + Vec3{0.0f, 0.0f, Data.HeightTiles * 5.0f} + Vec3{30.0f, -5.0f, 0.2f};
auto& door = level.PuzzleTiles.Get(DoorHandle);
door.EData.Visible = !IsSolved;
door.EData.Transform.Position =
WorldPosition + Vec3{0.0f, 0.0f, Data.HeightTiles * 5.0f} + Vec3{30.0f, -5.0f, 0.2f};
}
void Level::ReloadLevelEntities()
{
LevelEntities.Count = 0;
for (int32_t i = 0; i < BX_COUNTOF(BackgroundEntityHandles); ++i)
{
BackgroundEntityHandles[i] = LevelEntities.New();
auto& levelBgEntity = LevelEntities.Get(BackgroundEntityHandles[i]);
levelBgEntity.EData.LoadFromSaved(GetInstance().Player.Config.BackgroundLevelRenderData[i]);
}
}
int32_t GetNextRenderID()
{
static int32_t RenderIDCounter = 0;
RenderIDCounter++;
return RenderIDCounter;
}
} // namespace Game

View File

@@ -1,140 +1,29 @@
#pragma once
#include "../engine/Shared.h"
#include "Global.h"
#include "Log.h"
#include "Entity.h"
#include "Puzzle.h"
#include "UI.h"
#include "rendering/Rendering.h"
#include <bgfx/bgfx.h>
#include <cstdint>
#define ENTITY_HANDLE(X) \
struct X \
{ \
uint16_t Idx = UINT16_MAX; \
};
namespace Game
{
struct EntityRenderData
{
Vec4 TestColor{1.0f, 1.0f, 1.0f, 1.0f};
Vec4 BaseColor{0.0f, 0.0f, 0.0f, 1.0f};
Transform Transform;
uint16_t MaterialHandle = UINT16_MAX;
uint16_t ModelHandle = UINT16_MAX;
bool Visible = true;
void Render(const Model* models, const Material* materials);
};
ENTITY_HANDLE(CubeHandle);
struct Cube
{
int32_t TestX = -1;
int32_t TestY = -1;
EntityRenderData EData;
void Setup();
void Update();
};
ENTITY_HANDLE(TestEntityHandle);
struct TestEntity
{
EntityRenderData EData;
void Setup();
};
ENTITY_HANDLE(PuzzleTileEntityHandle);
struct PuzzleTileEntity
{
EntityRenderData EData;
};
ENTITY_HANDLE(UIQuadEntityHandle);
struct UIQuadEntity
{
EntityRenderData EData;
};
class IEntityManager
{
public:
virtual bool Setup(void*& ptr, bool forceReset) = 0;
};
template <typename T, typename HandleT, uint32_t C> class EntityManager : public IEntityManager
{
public:
uint16_t Count = 0;
T* Data = nullptr;
uint32_t EntitySize = 0;
T InvalidObject{};
bool IsEnabled = true;
public:
// Returns true if size changed
bool Setup(void*& ptr, bool forceReset)
{
bool changed = false;
if (EntitySize != sizeof(T) || forceReset)
{
Count = 0;
changed = true;
}
EntitySize = sizeof(T);
Data = reinterpret_cast<T*>(ptr);
ptr = (uint8_t*)ptr + (C * EntitySize);
return changed;
}
HandleT New()
{
if (Data == nullptr)
{
ERROR_ONCE("Accessed EntityManager before setup!");
return {};
}
if (Count >= C)
{
ERROR_ONCE("Too many entities!");
return {};
}
Data[Count] = {};
HandleT H;
H.Idx = Count;
++Count;
return H;
}
T& Get(HandleT handle)
{
if (handle.Idx > Count)
{
ERROR_ONCE("OOB Access!");
return InvalidObject;
}
return Data[handle.Idx];
}
void Render(const Model* models, const Material* materials)
{
if (!IsEnabled) return;
for (uint16_t i = 0; i < Count; ++i)
{
Get({i}).EData.Render(models, materials);
}
}
};
struct WorldPuzzle
{
static constexpr Vec2 WorldCardSize{10.0f, 10.0f};
Generated::PuzzleData Data;
Vec3 WorldPosition;
static constexpr Gen::Vec2 WorldCardSize{10.0f, 10.0f};
Gen::PuzzleData Data;
Gen::Vec3 WorldPosition;
PuzzleTileEntityHandle TileHandles[Puzzle::Config::MaxCardsInPuzzle];
UIQuadEntityHandle UIPlacedCards[Puzzle::Config::MaxCardsInPuzzle];
PuzzleTileCoverHandle CoverHandles[Puzzle::Config::MaxCardsInPuzzle * Puzzle::Config::MaxCoversInTile];
PuzzleTileEntityHandle EndHandles[Puzzle::Config::MaxPuzzleSizeCards];
PuzzleTileEntityHandle WallHandle;
PuzzleTileEntityHandle DoorHandle;
bool IsSetup = false;
bool IsActive = false;
bool IsSolved = false;
void Setup();
void Update();
@@ -145,16 +34,23 @@ namespace Game
public:
EntityManager<Cube, CubeHandle, 1024> Cubes;
EntityManager<TestEntity, TestEntityHandle, 32> Tests;
EntityManager<PuzzleTileEntity, PuzzleTileEntityHandle, 1024> PuzzleTiles;
EntityManager<UIQuadEntity, UIQuadEntityHandle, 1024> UIQuads;
EntityManager<PuzzleTileEntity, PuzzleTileEntityHandle, Puzzle::Config::MaxTilesTotal> PuzzleTiles;
EntityManager<PuzzleTileCover, PuzzleTileCoverHandle, Puzzle::Config::MaxCoversTotal> PuzzleTileCovers;
UIQuadEntityManager UIQuads;
EntityManager<LevelEntity, LevelEntityHandle, 64> LevelEntities;
CubeHandle PlayerOutsideViewCube;
LevelEntityHandle BackgroundEntityHandles[16];
public:
Generated::StaticPuzzleData PuzzleData;
WorldPuzzle Puzzles[1];
Gen::StaticPuzzleData PuzzleData;
WorldPuzzle Puzzles[Puzzle::Config::MaxVisiblePuzzles];
WorldPuzzleUI PuzzleUI;
public:
void Setup(GameData& data);
void Update();
void Render(uint16_t ViewID, const Model* models, const Material* materials);
void Render(uint16_t ViewID, const Model* models, const Material* materials, const Texture* textures);
void ReloadLevelEntities();
};
} // namespace Game

View File

@@ -1,10 +1,13 @@
#include "Log.h"
#include "bx/handlealloc.h"
#include "bx/hash.h"
#include "bx/timer.h"
#include <bx/string.h>
#define WIN32_LEAN_AND_MEAN
#include "Windows.h"
#include "Instance.h"
#define ESC "\x1B["
#define YELLOW ESC "33m"
#define RED ESC "31m"
@@ -12,9 +15,12 @@
namespace
{
char LineBuffer[1024]{0};
char OutBuffer[1024]{0};
char LineBuffer[LogInternal::MaxLineSize]{0};
char OutBuffer[LogInternal::MaxLineSize]{0};
char OutBufferUI[LogInternal::MaxLineSize]{0};
bx::HandleHashMapT<1024> OnceMap;
LogHistory History;
constexpr char LogFormat[]{"%s\n"};
constexpr char WarnFormat[]{YELLOW "%s" END "\n"};
constexpr char ErrorFormat[]{RED "%s" END "\n"};
@@ -31,8 +37,18 @@ void Log(ELogType logType, const char* file, uint32_t line, const char* format,
bx::snprintf(LineBuffer, sizeof(LineBuffer), LineFormat, format);
bx::vprintf(LineBuffer, args);
bx::vsnprintf(OutBuffer, sizeof(OutBuffer), LineBuffer, args);
bx::vsnprintf(OutBufferUI, sizeof(OutBufferUI), format, args);
va_end(args);
OutputDebugStringA(OutBuffer);
bx::strCopy(&History.LogBuffer[History.WriteIdx * LogInternal::MaxLineSize], LogInternal::MaxLineSize, OutBufferUI);
History.WriteTime[History.WriteIdx] = bx::getHPCounter();
History.WriteType[History.WriteIdx] = logType;
bx::strCopy(&History.FileBuffer[History.WriteIdx * LogInternal::MaxLineSize], LogInternal::MaxLineSize, file);
History.LineBuffer[History.WriteIdx] = line;
++History.WriteIdx;
if (History.WriteIdx >= LogInternal::LogHistorySize) History.WriteIdx = 0;
}
bool WasLogged(ELogType logType, const char* file, uint32_t line, const char* format)
@@ -48,3 +64,8 @@ bool WasLogged(ELogType logType, const char* file, uint32_t line, const char* fo
OnceMap.insert(hash, {});
return false;
}
LogHistory& GetLogHistory()
{
return History;
}

View File

@@ -27,5 +27,22 @@ enum class ELogType
Error,
};
namespace LogInternal
{
constexpr int32_t MaxLineSize = 1024;
constexpr int32_t LogHistorySize = 1024;
} // namespace LogInternal
struct LogHistory
{
char LogBuffer[LogInternal::MaxLineSize * LogInternal::LogHistorySize]{0};
char FileBuffer[LogInternal::MaxLineSize * LogInternal::LogHistorySize]{0};
uint32_t LineBuffer[LogInternal::LogHistorySize]{0};
int32_t WriteIdx = 0;
int64_t WriteTime[LogInternal::LogHistorySize]{0};
ELogType WriteType[LogInternal::LogHistorySize]{ELogType::Log};
};
void Log(ELogType logType, const char* file, uint32_t line, const char* format, ...);
bool WasLogged(ELogType logType, const char* file, uint32_t line, const char* format);
LogHistory& GetLogHistory();

View File

@@ -1,15 +1,19 @@
#include "Gen.h"
#include "Global.h"
#include "Log.h"
#include "Mesh.h"
#include "bgfx/bgfx.h"
#include "bx/bx.h"
#include "bx/error.h"
#include "bx/file.h"
#include "bx/filepath.h"
#include "bx/hash.h"
#include "bx/string.h"
#include "bx/timer.h"
#include "rendering/Rendering.h"
#include "Instance.h"
#include <cstdint>
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
@@ -20,6 +24,7 @@ namespace Game
{
bool LoadMesh(Model& mesh, const char* path, bool isBinary)
{
bx::strCopy(mesh.Name, sizeof(mesh.Name), path);
mesh.VertLayout.begin()
.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
.add(bgfx::Attrib::Normal, 3, bgfx::AttribType::Float)
@@ -57,23 +62,23 @@ namespace Game
tinygltf::Primitive primitive = model.meshes[0].primitives[0];
{
tinygltf::Accessor accessor = model.accessors.at(primitive.indices);
tinygltf::BufferView bufferView = model.bufferViews.at(accessor.bufferView);
tinygltf::Buffer buffer = model.buffers[bufferView.buffer];
const bgfx::Memory* ibMem = bgfx::alloc(bufferView.byteLength);
bx::memCopy(ibMem->data, &buffer.data.at(bufferView.byteOffset), bufferView.byteLength);
tinygltf::Accessor indexAccessor = model.accessors.at(primitive.indices);
tinygltf::BufferView indexBufferView = model.bufferViews.at(indexAccessor.bufferView);
int32_t indexStride = sizeof(uint16_t);
tinygltf::Buffer indexBuffer = model.buffers[indexBufferView.buffer];
const bgfx::Memory* ibMem = bgfx::alloc(indexBufferView.byteLength);
bx::memCopy(ibMem->data, &indexBuffer.data.at(indexBufferView.byteOffset), indexBufferView.byteLength);
mesh.IndexBuffer = bgfx::createIndexBuffer(ibMem);
}
{
tinygltf::Accessor posAccessor = model.accessors.at(primitive.attributes.at("POSITION"));
tinygltf::Accessor normalAccessor = model.accessors.at(primitive.attributes.at("NORMAL"));
tinygltf::Accessor uvAccessor = model.accessors.at(primitive.attributes.at("TEXCOORD_0"));
tinygltf::BufferView posBufferView = model.bufferViews[posAccessor.bufferView];
tinygltf::BufferView normalBufferView = model.bufferViews[normalAccessor.bufferView];
tinygltf::BufferView uvBufferView = model.bufferViews[uvAccessor.bufferView];
int posStride = posAccessor.ByteStride(posBufferView);
int normalStride = normalAccessor.ByteStride(normalBufferView);
int uvStride = uvAccessor.ByteStride(uvBufferView);
int32_t posStride = posAccessor.ByteStride(posBufferView);
int32_t normalStride = normalAccessor.ByteStride(normalBufferView);
int32_t uvStride = uvAccessor.ByteStride(uvBufferView);
tinygltf::Buffer posBuffer = model.buffers[posBufferView.buffer];
tinygltf::Buffer normalBuffer = model.buffers[normalBufferView.buffer];
tinygltf::Buffer uvBuffer = model.buffers[uvBufferView.buffer];
@@ -91,7 +96,113 @@ namespace Game
bx::memCopy(&v.uv_x, &uvBuffer.data.at(uvBufferView.byteOffset + i * uvStride), uvStride);
}
mesh.VertexBuffer = bgfx::createVertexBuffer(vbMem, mesh.VertLayout);
constexpr float SIZE_LIMIT = 1000.0f;
mesh.MinPos = {SIZE_LIMIT, SIZE_LIMIT, SIZE_LIMIT};
mesh.MaxPos = {-SIZE_LIMIT, -SIZE_LIMIT, -SIZE_LIMIT};
bx::memSet(mesh.Height.Values, 0, BX_COUNTOF(mesh.Height.Values));
int64_t startTime = bx::getHPCounter();
for (int32_t i = 0; i < vertexCount; ++i)
{
Gen::Vec3* pos =
reinterpret_cast<Gen::Vec3*>(&posBuffer.data[posBufferView.byteOffset + i * posStride]);
if (pos->x < mesh.MinPos.x) mesh.MinPos.x = pos->x;
if (pos->y < mesh.MinPos.y) mesh.MinPos.y = pos->y;
if (pos->z < mesh.MinPos.z) mesh.MinPos.z = pos->z;
if (pos->x > mesh.MaxPos.x) mesh.MaxPos.x = pos->x;
if (pos->y > mesh.MaxPos.y) mesh.MaxPos.y = pos->y;
if (pos->z > mesh.MaxPos.z) mesh.MaxPos.z = pos->z;
}
LOG("min/max: %lli", bx::getHPCounter() - startTime);
mesh.MinPos = {-5.0f, -5.0f, -5.0f};
mesh.MaxPos = {5.0f, 5.0f, 5.0f};
mesh.Size = mesh.MaxPos - mesh.MinPos;
startTime = bx::getHPCounter();
for (uint32_t v = 0; v < HeightMap::Height; ++v)
{
float vPos = mesh.MinPos.z + (float)v / HeightMap::Height * mesh.Size.z;
for (uint32_t u = 0; u < HeightMap::Width; ++u)
{
float uPos = mesh.MinPos.x + (float)u / HeightMap::Width * mesh.Size.x;
Gen::Vec3 rayStart = {uPos, -100.0f, vPos};
Gen::Vec3 rayEnd = {uPos, 100.0f, vPos};
Gen::Vec3 ptOut;
for (int16_t i = 0; i < indexBufferView.byteLength; i += indexStride * 3)
{
uint16_t* idxA = reinterpret_cast<uint16_t*>(&indexBuffer.data[indexBufferView.byteOffset + i]);
uint16_t* idxB = reinterpret_cast<uint16_t*>(
&indexBuffer.data[indexBufferView.byteOffset + i + 1 * indexStride]);
uint16_t* idxC = reinterpret_cast<uint16_t*>(
&indexBuffer.data[indexBufferView.byteOffset + i + 2 * indexStride]);
Gen::Vec3* triA =
reinterpret_cast<Gen::Vec3*>(&posBuffer.data[posBufferView.byteOffset + *idxA * posStride]);
Gen::Vec3* triB =
reinterpret_cast<Gen::Vec3*>(&posBuffer.data[posBufferView.byteOffset + *idxB * posStride]);
Gen::Vec3* triC =
reinterpret_cast<Gen::Vec3*>(&posBuffer.data[posBufferView.byteOffset + *idxC * posStride]);
if (Gen::RayTriangleIntersect(rayStart, rayEnd, *triA, *triB, *triC, ptOut))
{
float len = ptOut.y - rayStart.y;
uint8_t val = (uint8_t)(len / mesh.Size.y * UINT8_MAX);
int32_t idx = v * HeightMap::Width + u;
if (mesh.Height.Values[idx] < val)
{
mesh.Height.Values[idx] = val;
}
if (len < 0.0f)
{
LOG_ONCE("%f / %f = %u", len, mesh.Size.y, val);
}
}
}
}
}
LOG("heightmap: %lli", bx::getHPCounter() - startTime);
}
const bgfx::Memory* mem = bgfx::makeRef(&mesh.Height.Values[0], sizeof(mesh.Height.Values));
mesh.HeightMapTexture =
bgfx::createTexture2D(HeightMap::Width, HeightMap::Height, false, 1, bgfx::TextureFormat::R8, 0, mem);
for (auto& node : model.nodes)
{
if (bx::strFindI(node.name.c_str(), "_slot_").getLength() > 0)
{
if (mesh.SocketCount >= mesh.MaxSocketCount)
{
LOG_WARN("Too many sockets on mesh!");
break;
}
auto& socket = mesh.Sockets[mesh.SocketCount];
if (node.translation.size() >= 3)
{
socket.Pos.x = node.translation[0];
socket.Pos.y = node.translation[1];
socket.Pos.z = node.translation[2];
}
if (node.rotation.size() >= 4)
{
socket.Rot = Gen::RotationFromQuaternion({static_cast<float>(node.rotation[0]),
static_cast<float>(node.rotation[1]),
static_cast<float>(node.rotation[2]),
static_cast<float>(node.rotation[3])});
}
if (node.matrix.size() >= 0)
{
LOG_WARN("TODO: support matrix!");
}
bx::strCopy(&socket.Name[0], socket.MaxSocketNameLength, node.name.c_str());
++mesh.SocketCount;
}
}
return true;
}
@@ -133,6 +244,7 @@ namespace Game
}
LOG("Found %u models!", modelFilePathCount);
auto& inst = GetInstance();
int32_t writeI = 0;
for (int32_t i = 0; i < modelFilePathCount; ++i)
{
@@ -141,11 +253,11 @@ namespace Game
Model& mod = models[writeI];
if (LoadMesh(mod, fullPath.getCPtr(), modelFileIsBinary[i]))
{
mod.AssetHandle = CrcPath(fullPath.getCPtr());
auto& inst = GetInstance();
mod.Handle.Asset.Idx = CrcPath(fullPath.getCPtr());
mod.Handle.ModelIdx = writeI;
if (inst.DebugData.AssetCount < inst.DebugData.MaxAssets)
{
inst.DebugData.AssetHandles[inst.DebugData.AssetCount] = mod.AssetHandle;
inst.DebugData.AssetHandles[inst.DebugData.AssetCount] = mod.Handle.Asset;
bx::strCopy(inst.DebugData.AssetHandlePaths[inst.DebugData.AssetCount],
sizeof(inst.DebugData.AssetHandlePaths[inst.DebugData.AssetCount]),
fullPath.getCPtr());

View File

@@ -1,5 +1,6 @@
#include "../gen/Def.h"
#include "Gen.h"
#include "Global.h"
#include "Instance.h"
#include "Log.h"
#include "Puzzle.h"
@@ -7,24 +8,26 @@
#include "bx/string.h"
#include "imgui.h"
#include <cassert>
#include <cstdio>
namespace
{
static constexpr Generated::PuzPos Dirs[4]{
using namespace Gen;
static constexpr PuzPos Dirs[4]{
{-1, 0},
{0, -1},
{0, 1},
{1, 0},
};
Generated::StaticPuzzleData StaticData;
StaticPuzzleData StaticData;
StaticPuzzleCard InvalidCard;
PlacedPuzzleCard InvalidPlacedCard;
} // namespace
namespace Puzzle
{
constexpr float UIPuzBoxSize = 26;
using namespace Generated;
using namespace Gen;
void Setup()
{
@@ -40,8 +43,7 @@ namespace Puzzle
void LoadStaticPuzzleData()
{
Deserializer ser;
ser.Init("game/data/static/puzzle.dat");
if (ser.ReadT("SPUZ", StaticData))
if (ser.Init("game/data/static/puzzle.dat", "SPUZ") && ser.ReadT(StaticData))
{
LOG("Successfully loaded static puzzle data!");
}
@@ -52,8 +54,7 @@ namespace Puzzle
{
auto& data = GetStaticPuzzleData();
Serializer ser;
ser.Init("game/data/static/puzzle.dat");
if (ser.WriteT("SPUZ", GetStaticPuzzleData()))
if (ser.Init("game/data/static/puzzle.dat", "SPUZ") && ser.WriteT(GetStaticPuzzleData()))
{
LOG("Successfully saved static puzzle data!");
}
@@ -61,7 +62,11 @@ namespace Puzzle
}
const StaticPuzzleCard& GetCard(StaticPuzzleCardHandle H)
{
assert(IsValid(H));
if (H.Idx == UINT16_MAX)
{
LOG_ERROR("Invalid static card handle!");
return InvalidCard;
}
return GetStaticPuzzleData().Cards[H.Idx];
}
@@ -83,22 +88,38 @@ namespace Puzzle
PuzzleElementType::Enum GetNodeAt(const PuzzleData& puz, PuzPos pos)
{
assert(pos.X < Puzzle::Config::MaxPuzzleSizeCards && pos.Y < Puzzle::Config::MaxPuzzleSizeCards && pos.X >= 0 &&
assert(pos.X < Puzzle::Config::MaxPuzzleSizeTiles && pos.Y < Puzzle::Config::MaxPuzzleSizeTiles && pos.X >= 0 &&
pos.Y >= 0);
// TODO: this is horrible
for (int32_t i = 0; i < puz.PlacedCardCount; ++i)
{
auto& card = puz.PlacedCards[i];
int8_t offsetX = pos.X - card.Position.X;
int8_t offsetY = pos.Y - card.Position.Y;
if (offsetX >= 0 && offsetX < Puzzle::Config::CardSize && offsetY >= 0 &&
offsetY < Puzzle::Config::CardSize)
int32_t cardIdxX = pos.X / Puzzle::Config::CardSize;
int32_t cardIdxY = pos.Y / Puzzle::Config::CardSize;
auto& card = puz.PlacedCards[cardIdxY * Puzzle::Config::MaxPuzzleSizeCards + cardIdxX];
int32_t offsetX = pos.X - (cardIdxX * Config::CardSize);
int32_t offsetY = pos.Y - (cardIdxY * Config::CardSize);
if (offsetX >= 0 && offsetX < Puzzle::Config::CardSize && offsetY >= 0 && offsetY < Puzzle::Config::CardSize &&
IsValid(card.RefCard))
{
PuzzleElementType::Enum cardVal = GetCardNodeAt(GetCard(card.RefCard), card.Rotation, offsetX, offsetY);
if (cardVal != PuzzleElementType::None) return cardVal;
}
return puz.BackgroundTiles[pos.Y * Puzzle::Config::MaxPuzzleSizeTiles + pos.X];
}
return puz.BackgroundTiles[pos.Y * Puzzle::Config::MaxPuzzleSizeCards + pos.X];
PuzzleElementType::Enum GetInitialNodeAt(const PuzzleData& puz, PuzPos pos)
{
assert(pos.X < Puzzle::Config::MaxPuzzleSizeTiles && pos.Y < Puzzle::Config::MaxPuzzleSizeTiles && pos.X >= 0 &&
pos.Y >= 0);
int32_t cardIdxX = pos.X / Puzzle::Config::CardSize;
int32_t cardIdxY = pos.Y / Puzzle::Config::CardSize;
auto& card = puz.InitialPlacedCards[cardIdxY * Puzzle::Config::MaxPuzzleSizeCards + cardIdxX];
int32_t offsetX = pos.X - (cardIdxX * Config::CardSize);
int32_t offsetY = pos.Y - (cardIdxY * Config::CardSize);
if (offsetX >= 0 && offsetX < Puzzle::Config::CardSize && offsetY >= 0 && offsetY < Puzzle::Config::CardSize &&
IsValid(card.RefCard))
{
PuzzleElementType::Enum cardVal = GetCardNodeAt(GetCard(card.RefCard), card.Rotation, offsetX, offsetY);
if (cardVal != PuzzleElementType::None) return cardVal;
}
return puz.BackgroundTiles[pos.Y * Puzzle::Config::MaxPuzzleSizeTiles + pos.X];
}
PuzzleElementType::Enum GetCardNodeAt(const StaticPuzzleCard& card, uint8_t rotation, int8_t x, int8_t y)
@@ -170,6 +191,11 @@ namespace Puzzle
bool PuzzleSolver::IsExitSatisfied(const PuzzleData& puzzle, PuzPos pos)
{
PuzzleElementType::Enum goalType = GetNodeAt(puzzle, pos);
if (goalType == PuzzleElementType::None)
{
WARN_ONCE("TODO!");
return false;
}
uint32_t currentPositionQueueIdx = 0;
uint32_t positionQueueCount = 0;
@@ -177,23 +203,38 @@ namespace Puzzle
positionQueue[0] = pos;
positionQueueCount++;
while (positionQueueCount > 0)
while (currentPositionQueueIdx < positionQueueCount)
{
assert(currentPositionQueueIdx < Puzzle::Config::MaxTilesInPuzzle);
PuzPos currentPos = positionQueue[currentPositionQueueIdx];
for (PuzPos dir : Dirs)
{
PuzPos nextPos = currentPos + dir;
if (!(nextPos.X >= 0 && nextPos.Y >= 0 && nextPos.X < puzzle.WidthTiles &&
nextPos.Y < puzzle.HeightTiles))
continue;
if (IsValidGoalConnection(puzzle, nextPos, currentPos, goalType))
{
if (IsValidSource(GetNodeAt(puzzle, nextPos), goalType))
{
return true;
}
bool found = false;
for (int32_t i = 0; i < positionQueueCount; ++i)
{
if (positionQueue[i].X == nextPos.X && positionQueue[i].Y == nextPos.Y)
{
found = true;
break;
}
}
if (!found)
{
positionQueue[positionQueueCount] = nextPos;
positionQueueCount++;
}
}
}
currentPositionQueueIdx++;
}
return false;
@@ -210,14 +251,14 @@ namespace Puzzle
if (goalType == PuzzleElementType::WaterGoal)
{
return from == PuzzleElementType::WaterIn || from == PuzzleElementType::WaterChannel ||
from == PuzzleElementType::WaterGoal;
from == PuzzleElementType::WaterGoal || from == PuzzleElementType::Bridge;
}
else if (goalType == PuzzleElementType::ElectricGoal)
{
return from == PuzzleElementType::ElectricIn || from == PuzzleElementType::ElectricGoal ||
from == PuzzleElementType::None;
}
assert(false);
// assert(false);
return false;
}
bool PuzzleSolver::IsValidSource(PuzzleElementType::Enum sourceType, PuzzleElementType::Enum goalType)
@@ -254,16 +295,128 @@ namespace Puzzle
ImGui::InvisibleButton("cardbn",
{Puzzle::Config::CardSize * UIPuzBoxSize, Puzzle::Config::CardSize * UIPuzBoxSize});
}
void RotateCard(PlacedPuzzleCard& card)
{
card.Rotation += 1;
if (card.Rotation >= 4) card.Rotation = 0;
}
void WritePuzzleFilePath(char* buf, int32_t bufSize, uint16_t puzID)
{
bx::snprintf(buf, bufSize, "%s/%u.pzl", Puzzle::PuzzleFileDir, puzID);
}
PlacedPuzzleCard& GetCardAt(PuzzleData& obj, PuzPos pos)
{
if (pos.X < 0 || pos.X >= Config::MaxPuzzleSizeCards || pos.Y < 0 || pos.Y >= Config::MaxPuzzleSizeCards)
{
LOG_ERROR("Invalid card access at %i %i!!", pos.X, pos.Y);
return InvalidPlacedCard;
}
return obj.PlacedCards[pos.Y * Config::MaxPuzzleSizeCards + pos.X];
}
PlacedPuzzleCard& GetInitialCardAt(PuzzleData& obj, PuzPos pos)
{
if (pos.X < 0 || pos.X >= Config::MaxPuzzleSizeCards || pos.Y < 0 || pos.Y >= Config::MaxPuzzleSizeCards)
{
LOG_ERROR("Invalid card access at %i %i!!", pos.X, pos.Y);
return InvalidPlacedCard;
}
return obj.InitialPlacedCards[pos.Y * Config::MaxPuzzleSizeCards + pos.X];
}
bool ReturnPlacedCard(PuzzleData& obj, PuzPos targetPos)
{
PlacedPuzzleCard& placedCard = GetCardAt(obj, targetPos);
if (IsValid(placedCard.RefCard))
{
if (GetFlag(placedCard.Flags, PlacedPuzzleCardFlags::Locked))
{
LOG_WARN("Card at %i %i is locked!", targetPos.X, targetPos.Y);
return false;
}
bool found = false;
for (int32_t i = 0; i < obj.AvailableCardCount; ++i)
{
if (obj.AvailableCards[i].RefCard.Idx == placedCard.RefCard.Idx && obj.AvailableCards[i].UsedCount > 0)
{
obj.AvailableCards[i].UsedCount--;
found = true;
break;
}
}
if (!found)
{
LOG_ERROR("Failed to find available card to return placed card of type %u to!", placedCard.RefCard.Idx);
return false;
}
placedCard.RefCard = {};
}
return true;
}
bool DragAvailableCardTo(PuzzleData& obj, PuzPos targetPos, int32_t availIdx, uint8_t rotation)
{
if (availIdx >= obj.AvailableCardCount)
{
LOG_ERROR("Invalid drag with avail idx %i!", availIdx);
return false;
}
if (!ReturnPlacedCard(obj, targetPos)) return false;
auto& draggedCard = obj.AvailableCards[availIdx];
draggedCard.UsedCount++;
PlacedPuzzleCard& placedCard = GetCardAt(obj, targetPos);
placedCard.RefCard = draggedCard.RefCard;
placedCard.Flags = (PlacedPuzzleCardFlags::Enum)ClearFlags(placedCard.Flags, PlacedPuzzleCardFlags::Locked);
placedCard.Position = targetPos;
placedCard.Rotation = rotation;
return true;
}
void RecalculateInitialAvailable(PuzzleData& obj)
{
for (int32_t i = 0; i < obj.AvailableCardCount; ++i)
{
obj.AvailableCards[i].UsedCount = obj.AvailableCards[i].MaxAvailableCount;
}
for (int32_t y = 0; y < obj.HeightTiles; ++y)
{
for (int32_t x = 0; x < obj.WidthTiles; ++x)
{
auto& placedCard = obj.PlacedCards[y * Puzzle::Config::MaxPuzzleSizeCards + x];
if (Puzzle::IsValid(placedCard.RefCard))
{
bool found = false;
for (int32_t i = 0; i < obj.AvailableCardCount; ++i)
{
if (obj.AvailableCards[i].RefCard.Idx == placedCard.RefCard.Idx)
{
found = true;
obj.AvailableCards[i].MaxAvailableCount++;
}
}
if (!found)
{
if (obj.AvailableCardCount == Puzzle::Config::MaxAvailableStacks)
{
LOG_ERROR("Read limit of available card stacks!");
break;
}
obj.AvailableCards[obj.AvailableCardCount] = {};
obj.AvailableCards[obj.AvailableCardCount].RefCard = placedCard.RefCard;
obj.AvailableCards[obj.AvailableCardCount].MaxAvailableCount = 1;
}
}
}
}
}
bool RenderDebugUI(PuzzleData& obj)
{
bool dataChanged = false;
bool isVisible = true;
if (ImGui::Begin("Puzzle", &isVisible))
{
@@ -271,23 +424,40 @@ namespace Puzzle
{
char filepath[128]{0};
WritePuzzleFilePath(filepath, sizeof(filepath), obj.ID);
remove(filepath);
bx::remove(filepath);
obj.ID = UINT16_MAX;
ImGui::End();
return false;
}
ImGui::SameLine();
if (ImGui::Button("Reload"))
if (ImGui::Button("Reset"))
{
// TODO
ResetPuzzle(obj);
}
ImGui::SameLine();
if (ImGui::Button("Save"))
{
ResetPuzzle(obj);
char path[128]{0};
WritePuzzleFilePath(path, sizeof(path), obj.ID);
Serializer ser;
if (ser.Init(path, "PZZL") && ser.WriteT(obj))
{
LOG("Saved to %s", path);
}
else
{
LOG_ERROR("Failed to save to %s", path);
}
ser.Finish();
}
int32_t val = obj.ID;
if (ImGui::InputInt("ID", &val))
{
obj.ID = val;
dataChanged = true;
}
dataChanged = ImGui::InputText("Name", obj.PuzzleName, sizeof(obj.PuzzleName)) || dataChanged;
ImGui::InputText("Name", obj.PuzzleName, sizeof(obj.PuzzleName));
int32_t W = obj.WidthTiles;
int32_t H = obj.HeightTiles;
@@ -296,7 +466,6 @@ namespace Puzzle
if (ImGui::DragInt("", &W, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
{
obj.WidthTiles = uint8_t(W);
dataChanged = true;
}
ImGui::PopID();
ImGui::SameLine();
@@ -307,34 +476,76 @@ namespace Puzzle
if (ImGui::DragInt("", &H, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
{
obj.HeightTiles = uint8_t(H);
dataChanged = true;
}
ImGui::PopID();
ImVec2 puzCursorStart = ImGui::GetCursorScreenPos();
auto& drawList = *ImGui::GetWindowDrawList();
obj.GoalPositionCount = 0;
for (int32_t y = 0; y < obj.HeightTiles; ++y)
{
ImGui::PushID(y);
for (int32_t x = 0; x < obj.WidthTiles; ++x)
{
ImGui::PushID(x);
auto node = GetNodeAt(obj, {int8_t(x), int8_t(y)});
PuzPos nodePos = {int8_t(x), int8_t(y)};
auto node = GetInitialNodeAt(obj, nodePos);
if (node == PuzzleElementType::WaterGoal)
{
obj.GoalPositions[obj.GoalPositionCount] = nodePos;
obj.GoalPositionCount++;
}
ImVec2 pos = ImVec2{puzCursorStart.x + x * UIPuzBoxSize + 5, puzCursorStart.y + y * UIPuzBoxSize};
ImGui::SetCursorScreenPos(pos);
ImGui::Text("%s", GetShortNodeName(node));
ImGui::SetCursorScreenPos(pos);
if (x % Puzzle::Config::CardSize == 0 && y % Puzzle::Config::CardSize == 0)
{
PuzPos cardPos = {int8_t(x / Config::CardSize), int8_t(y / Config::CardSize)};
PlacedPuzzleCard& placedCard = GetInitialCardAt(obj, cardPos);
bool isLocked = GetFlag(placedCard.Flags, PlacedPuzzleCardFlags::Locked);
ImGui::InvisibleButton("bn", {UIPuzBoxSize * 2, UIPuzBoxSize * 2});
if (ImGui::IsItemClicked(ImGuiMouseButton_Right))
{
placedCard.Rotation += 1;
if (placedCard.Rotation >= 4) placedCard.Rotation = 0;
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Middle))
{
if (ImGui::IsKeyDown(ImGuiKey_LeftShift))
{
placedCard = {};
}
else
{
placedCard.Flags =
(PlacedPuzzleCardFlags::Enum)(placedCard.Flags ^ PlacedPuzzleCardFlags::Locked);
RecalculateInitialAvailable(obj);
}
}
if (!isLocked && IsValid(placedCard.RefCard))
{
ImVec2 s = {puzCursorStart.x + x * UIPuzBoxSize, puzCursorStart.y + y * UIPuzBoxSize};
drawList.AddRectFilled(s,
{s.x + UIPuzBoxSize * Puzzle::Config::CardSize,
s.y + UIPuzBoxSize * Puzzle::Config::CardSize},
0x33FFFFFF);
}
if (ImGui::BeginDragDropTarget())
{
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("cardtype", 0))
{
uint32_t CardIdx = *reinterpret_cast<uint32_t*>(payload->Data);
obj.PlacedCards[obj.PlacedCardCount].RefCard = {(uint16_t)CardIdx};
obj.PlacedCardCount++;
placedCard = {};
placedCard.RefCard = {(uint16_t)CardIdx};
placedCard.Rotation = 0;
placedCard.Position = cardPos;
placedCard.Flags = (PlacedPuzzleCardFlags::Enum)SetFlags(placedCard.Flags,
PlacedPuzzleCardFlags::Locked);
RecalculateInitialAvailable(obj);
}
ImGui::EndDragDropTarget();
}
@@ -364,6 +575,11 @@ namespace Puzzle
ImGui::SetCursorScreenPos(pos);
ImGui::NewLine();
ImGui::Separator();
PuzzleSolver solver;
ImGui::Text("Solved: %s", solver.IsPuzzleSolved(obj) ? "yes!" : "no.");
bool bAvailOpen =
ImGui::TreeNodeEx("Available Cards", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed);
if (ImGui::BeginDragDropTarget())
@@ -395,72 +611,57 @@ namespace Puzzle
obj.AvailableCardCount++;
}
}
dataChanged = true;
}
ImGui::EndDragDropTarget();
}
if (bAvailOpen)
{
uint8_t debugRot = Game::GetInstance().DebugData.DebugCardRotation;
ImVec2 startPos = ImGui::GetCursorScreenPos();
for (uint32_t i = 0; i < obj.AvailableCardCount; ++i)
{
ImVec2 localPos = {startPos.x + i * 60.0f, startPos.y};
ImGui::SetCursorScreenPos(localPos);
ImGui::PushID(i);
auto& card = obj.AvailableCards[i];
DrawCard(GetCard(card.RefCard), debugRot, ImGui::GetCursorScreenPos());
if (ImGui::BeginDragDropSource())
DrawCard(GetCard(card.RefCard), 0, ImGui::GetCursorScreenPos());
int displayCount = card.MaxAvailableCount - card.UsedCount;
ImGui::SetCursorScreenPos({localPos.x, localPos.y + 55.0f});
ImGui::SetNextItemWidth(35);
ImGui::PushID("carc");
if (ImGui::DragInt("", &displayCount, 0.25f))
{
ImGui::SetDragDropPayload("availcard", &i, sizeof(i));
DrawCard(GetCard(card.RefCard), debugRot, ImGui::GetCursorScreenPos());
ImGui::EndDragDropSource();
int diff = displayCount - (card.MaxAvailableCount - card.UsedCount);
card.MaxAvailableCount = bx::max(0, card.MaxAvailableCount + diff);
}
ImGui::PopID();
ImGui::SameLine(0, 3);
if (ImGui::Button("x"))
{
if (i < obj.AvailableCardCount - 1)
{
obj.AvailableCards[i] = obj.AvailableCards[obj.AvailableCardCount - 1];
}
obj.AvailableCardCount--;
}
ImGui::PopID();
}
ImGui::TreePop();
}
}
ImGui::End();
if (dataChanged)
{
char path[128]{0};
WritePuzzleFilePath(path, sizeof(path), obj.ID);
Serializer ser;
ser.Init(path);
if (ser.WriteT("PZZL", obj))
{
LOG("Saved to %s", path);
}
else
{
LOG_ERROR("Failed to save to %s", path);
}
ser.Finish();
}
return isVisible;
}
void ResetPuzzle(PuzzleData& puz)
{
for (int32_t i = 0; i < puz.AvailableCardCount; ++i)
{
puz.AvailableCards[i].UsedCount = 0;
}
for (int32_t i = 0; i < BX_COUNTOF(puz.PlacedCards); ++i)
{
puz.PlacedCards[i] = puz.InitialPlacedCards[i];
}
}
} // namespace Puzzle
namespace Generated
{
PuzPos operator+=(PuzPos lhs, const PuzPos& rhs)
{
lhs.X += rhs.X;
lhs.Y += rhs.Y;
return lhs;
}
PuzPos operator+(PuzPos lhs, const PuzPos& rhs)
{
lhs += rhs;
return lhs;
}
PuzPos operator-=(PuzPos lhs, const PuzPos& rhs)
{
lhs.X -= rhs.X;
lhs.Y -= rhs.Y;
return lhs;
}
PuzPos operator-(PuzPos lhs, const PuzPos& rhs)
{
lhs -= rhs;
return lhs;
}
} // namespace Generated

View File

@@ -1,27 +1,31 @@
#pragma once
#include "Serial.h"
#include <cstdint>
#include <imgui.h>
#include "../../gen/Generated.h"
#include "../gen/Generated.h"
namespace Puzzle
{
using namespace Generated;
using namespace Gen;
constexpr const char* PuzzleFileDir = "game/data/puzzles/";
struct Config
{
static constexpr uint32_t MaxVisiblePuzzles = 3;
static constexpr uint32_t CardSize = 2;
static constexpr uint32_t NodesPerCard = CardSize * CardSize;
static constexpr uint32_t MaxElementsPerTile = 4;
static constexpr uint32_t MaxPuzzleSizeCards = 16;
static constexpr uint32_t MaxCardsInPuzzle = MaxPuzzleSizeCards * MaxPuzzleSizeCards;
static constexpr uint32_t MaxPuzzleSizeTiles = 16 * CardSize;
static constexpr uint32_t MaxTilesInPuzzle = MaxPuzzleSizeTiles * MaxPuzzleSizeTiles;
static constexpr uint32_t MaxTilesTotal =
MaxTilesInPuzzle * MaxVisiblePuzzles + MaxPuzzleSizeCards * MaxVisiblePuzzles + 64;
static constexpr uint32_t MaxAvailableStacks = 16;
static constexpr uint32_t MaxGoalPositions = 16;
static constexpr float CardScaleWorld = 10.0f;
static constexpr uint32_t MaxCoversInTile = 8;
static constexpr uint32_t MaxCoversTotal = MaxCoversInTile * MaxTilesTotal;
};
void Setup();
@@ -32,17 +36,27 @@ namespace Puzzle
bool IsValid(StaticPuzzleCardHandle h);
uint8_t GetRemainingCount(const PuzzleCardStack& stack);
PuzzleElementType::Enum GetNodeAt(const PuzzleData& puz, PuzPos pos);
PuzzleElementType::Enum GetInitialNodeAt(const PuzzleData& puz, PuzPos pos);
PuzzleElementType::Enum GetCardNodeAt(const StaticPuzzleCard& card, uint8_t rotation, int8_t x, int8_t y);
PuzzleElementType::Enum& EditCardNodeAt(StaticPuzzleCard& card, uint8_t rotation, int8_t x, int8_t y);
void DrawCard(const StaticPuzzleCard& card, uint8_t rotation, ImVec2 pos);
void RotateCard(PlacedPuzzleCard& card);
PlacedPuzzleCard& GetCardAt(PuzzleData& obj, PuzPos pos);
PlacedPuzzleCard& GetInitialCardAt(PuzzleData& obj, PuzPos pos);
// TODO: targetPos is of type CardPos
bool ReturnPlacedCard(PuzzleData& obj, PuzPos targetPos);
// TODO: targetPos is of type CardPos
bool DragAvailableCardTo(PuzzleData& obj, PuzPos targetPos, int32_t availIdx, uint8_t rotation);
void ResetPuzzle(PuzzleData& puz);
struct PuzzleSolver
{
bool IsPuzzleSolved(const Generated::PuzzleData& puzzle);
bool IsExitSatisfied(const Generated::PuzzleData& puzzle, PuzPos pos);
bool IsPuzzleSolved(const PuzzleData& puzzle);
bool IsExitSatisfied(const PuzzleData& puzzle, PuzPos pos);
// This assumes flowFrom is already verified to be connected.
bool IsValidGoalConnection(const Generated::PuzzleData& puzzle,
bool IsValidGoalConnection(const PuzzleData& puzzle,
PuzPos flowFrom,
PuzPos flowTo,
PuzzleElementType::Enum goalType);
@@ -50,9 +64,3 @@ namespace Puzzle
};
bool RenderDebugUI(PuzzleData& obj);
} // namespace Puzzle
namespace Generated
{
PuzPos operator+=(PuzPos lhs, const PuzPos& rhs);
PuzPos operator+(PuzPos lhs, const PuzPos& rhs);
} // namespace Generated

View File

@@ -1,32 +0,0 @@
#include "Global.h"
#include "Log.h"
#include "Serial.h"
#include "bx/bx.h"
#include "bx/file.h"
#include "bx/filepath.h"
void SerializeStruct(void* data, uint64_t size, const char* path)
{
bx::Error err;
bx::FilePath filePath{path};
bx::FileWriter writer;
if (writer.open(filePath, false, &err))
{
if (!writer.write(data, size, &err))
{
LOG_ERROR("Failed to write to file %s: %s", path, err.getMessage().getCPtr());
writer.close();
return;
}
}
else
{
LOG_ERROR("Failed to open file %s: %s", path, err.getMessage().getCPtr());
return;
}
LOG("Successful serialization");
}
void DeserializeStruct(void* data, SerializationHeader& expectedHeader, const char* path)
{
}

View File

@@ -1,32 +0,0 @@
#include <cstdint>
struct SerializationHeader
{
uint8_t _ID0 = 0;
uint8_t _ID1 = 0;
uint8_t _ID2 = 0;
uint8_t _ID3 = 0;
uint8_t HeaderVersion = 0;
uint8_t VersionNum = 0;
uint8_t Reserved0 = 0;
uint8_t Reserved1 = 0;
};
#define SER_HEADER(Version, FCC) \
SerializationHeader __Header{ \
FCC[0], \
FCC[1], \
FCC[2], \
FCC[3], \
1, \
Version, \
0, \
0, \
}; \
bool VersionMatches(uint8_t headerVersion, uint8_t versionNum) \
{ \
return headerVersion == 1 && versionNum == Version; \
}
void SerializeStruct(void* data, uint64_t size, const char* path);
void DeserializeStruct(void* data, SerializationHeader& expectedHeader, const char* path);

View File

@@ -1,14 +1,16 @@
#include "Global.h"
#include "Input.h"
#include "Instance.h"
#include "Log.h"
#include "Setup.h"
#include "Tools.h"
#include "rendering/Rendering.h"
#include "bx/bx.h"
#include "bx/timer.h"
#ifdef TRACY_ENABLE
#include <client/TracyProfiler.hpp>
#include <tracy/tracy.hpp>
#endif
#include <tracy/Tracy.hpp>
namespace Game
{
@@ -26,34 +28,37 @@ namespace Game
void Setup(SharedData& shared)
{
LOG("Game Setup Start!");
#ifdef TRACY_ENABLE
LOG("Tracy is enabled");
tracy::StartupProfiler();
#endif
if (shared.Game.PermanentStorage == nullptr)
if (shared.Game.PermanentArena.Base == nullptr)
{
LOG_ERROR("Game memory not initialized!!");
return;
}
if (shared.Game.EntityStorage == nullptr)
if (shared.Game.EntityArena.Base == nullptr)
{
LOG_ERROR("Entity memory not initialized!");
return;
}
if (shared.Game.PermanentStorageSize < sizeof(GameInstance))
if (shared.Game.PermanentArena.MaxSize < sizeof(GameInstance))
{
LOG_ERROR("Game memory too small! %u < %u", shared.Game.PermanentStorageSize, sizeof(GameInstance));
LOG_ERROR("Game memory too small! %u < %u", shared.Game.PermanentArena.MaxSize, sizeof(GameInstance));
return;
}
GameInstance& instance = *reinterpret_cast<GameInstance*>(shared.Game.PermanentStorage);
GameInstance& instance = *reinterpret_cast<GameInstance*>(shared.Game.PermanentArena.Base);
if (sizeof(GameInstance) != instance.Size)
{
LOG_WARN("Game instance size changed, resetting!");
instance = {};
}
instance.UsedScratchAmount = 0;
SetShared(shared);
SetInstance(instance);
ResetScratch();
Puzzle::LoadStaticPuzzleData();
SetupInstance.Rendering.Setup();
SetupInstance.Rendering.Setup({});
instance.GameLevel.Setup(shared.Game);
instance.IsInitialized = true;
}
@@ -82,6 +87,7 @@ namespace Game
bx::memCopy(win.LastHeldScanCodes, win.HeldScanCodes, sizeof(win.HeldScanCodes));
bx::memCopy(win.LastHeldMouseButtons, win.HeldMouseButtons, sizeof(win.HeldMouseButtons));
}
Tools::MeasureFrameEnd();
FrameMark;
}
@@ -89,6 +95,8 @@ namespace Game
{
LOG("Shutdown");
SetupInstance.Rendering.Shutdown();
#ifdef TRACY_ENABLE
tracy::ShutdownProfiler();
#endif
}
} // namespace Game

View File

@@ -1,13 +1,32 @@
#include "../gen/Def.h"
#include "Gen.h"
#include "Global.h"
#include "Instance.h"
#include "Log.h"
#include "Mesh.h"
#include "Puzzle.h"
#include "Tools.h"
#include "rendering/Rendering.h"
#include "bgfx/bgfx.h"
#include "bx/filepath.h"
#include "bx/string.h"
#include "bx/timer.h"
#include "rendering/Rendering.h"
#include <imgui.h>
#include <tracy/Tracy.hpp>
using namespace Gen;
namespace
{
constexpr int32_t FrameTimeBufSize = 512;
int64_t FrameTimes[FrameTimeBufSize]{0};
int32_t FrameTimeIdx = 0;
} // namespace
namespace Tools
{
const char* GetAssetPath(uint32_t assetHandle)
const char* GetAssetPath(Gen::AssetHandle assetHandle)
{
const auto& inst = Game::GetInstance();
for (int32_t j = 0; j < inst.DebugData.AssetCount; ++j)
@@ -20,20 +39,722 @@ namespace Tools
return "---";
}
void ModelDropdown(uint16_t& modelHandle)
bool EntityDataSettings(Gen::SavedEntityRenderData& data)
{
ImGui::PushID(&data);
bool changed = false;
changed |= ModelDropdown(data.Model);
changed |= MaterialDropdown(data.Material);
changed |= TextureDropdown(data.Texture);
changed |= TransformUI(data.TF);
changed |= ImGui::Checkbox("Visible", &data.Visible);
changed |= ImGui::ColorEdit4("Color 1", &data.BaseColor.x);
changed |= ImGui::ColorEdit4("Color 2", &data.HighlightColor.x);
ImGui::PopID();
return changed;
}
bool ModelDropdown(Gen::ModelHandle& modelHandle, const char* title)
{
bool changed = false;
auto& R = Game::GameRendering::Get();
const char* name = GetAssetPath(R.Models[modelHandle].AssetHandle);
if (ImGui::BeginCombo("Models", name))
const char* assetName = GetAssetPath(modelHandle.Asset);
if (ImGui::BeginCombo(title, assetName))
{
for (int32_t i = 0; i < R.ModelCount; ++i)
{
if (ImGui::Selectable(GetAssetPath(R.Models[i].AssetHandle), i == modelHandle))
if (ImGui::Selectable(GetAssetPath(R.Models[i].Handle.Asset), i == modelHandle.ModelIdx))
{
modelHandle = i;
modelHandle = R.Models[i].Handle;
changed = true;
}
}
ImGui::EndCombo();
}
return changed;
}
bool TextureDropdown(Gen::TextureHandle& texHandle, const char* title)
{
bool changed = false;
auto& R = Game::GameRendering::Get();
const char* name = GetAssetPath(texHandle.Asset);
if (ImGui::BeginCombo(title, name))
{
for (int32_t i = 0; i < R.MaxTextures; ++i)
{
if (!IsValid(R.Textures[i].TexHandle)) continue;
ImGui::PushID(i);
ImVec2 pos = ImGui::GetCursorScreenPos();
if (ImGui::Selectable("", i == texHandle.TextureIdx, ImGuiSelectableFlags_AllowOverlap, {0, 64}))
{
texHandle = R.Textures[i].TexHandle;
changed = true;
}
ImGui::SetCursorScreenPos(pos);
ImGui::Image(R.Textures[i].RenderHandle.idx, {64, 64});
ImGui::SameLine();
ImGui::Text("%s", GetAssetPath(R.Textures[i].TexHandle.Asset));
ImGui::PopID();
}
ImGui::EndCombo();
}
return changed;
}
bool MaterialDropdown(Gen::EMaterial::Enum& material)
{
bool changed = false;
const char* selectedText = "---";
if (material < Gen::EMaterial::EntryCount)
{
selectedText = Gen::EMaterial::EntryNames[material];
}
if (ImGui::BeginCombo("Material", selectedText))
{
for (int32_t i = 0; i < Gen::EMaterial::EntryCount; ++i)
{
if (ImGui::Selectable(Gen::EMaterial::EntryNames[i], i == material))
{
material = (Gen::EMaterial::Enum)i;
changed = true;
}
}
ImGui::EndCombo();
}
return changed;
}
constexpr const char* UnitStrings[]{
"b",
"kb",
"mb",
"gb",
};
const char* GetUnitString(uint64_t byteCount, uint64_t& outCount)
{
outCount = byteCount;
int32_t strIdx = 0;
for (int32_t i = 0; i < BX_COUNTOF(UnitStrings); ++i)
{
if (outCount < 1024) break;
++strIdx;
outCount /= 1024;
}
return UnitStrings[strIdx];
}
void ProgressBar(const char* title, uint64_t current, uint64_t max)
{
ImGui::PushID(title);
float percent = static_cast<double>(current) / static_cast<double>(max);
ImVec2 startPos = ImGui::GetCursorScreenPos();
char content[128]{0};
uint64_t currentUnit = 0;
const char* currentUnitStr = GetUnitString(current, currentUnit);
uint64_t maxUnit = 0;
const char* maxUnitStr = GetUnitString(max, maxUnit);
bx::snprintf(content,
sizeof(content),
"%s: %u %s/%u %s (%.2f%%)",
title,
currentUnit,
currentUnitStr,
maxUnit,
maxUnitStr,
percent);
ImGui::Text("%s", content);
ImGui::PopID();
}
bool TransformUI(Gen::Transform& transform)
{
bool changed = false;
changed |= ImGui::DragFloat3("Pos", &transform.Position.x, 0.1f);
Vec3 euler = EulerFromRotation(transform.Rotation);
if (ImGui::DragFloat3("Rot", &euler.x, 0.1f))
{
transform.Rotation = RotationFromEuler(euler);
changed = true;
}
changed |= ImGui::DragFloat3("Scale", &transform.Scale.x, 0.01f);
return changed;
}
void RenderLogUI()
{
auto& time = Game::GetInstance().Time;
auto& debug = Game::GetInstance().DebugData;
if (ImGui::Begin("Log"))
{
ImGui::Checkbox("Shorten File Names", &debug.ShortenLogFileNames);
ImGui::BeginTable("tbl",
4,
ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_SizingFixedFit |
ImGuiTableFlags_RowBg);
ImGui::TableSetupColumn("Time", ImGuiTableColumnFlags_NoResize);
ImGui::TableSetupColumn("Log");
ImGui::TableSetupColumn("Line", ImGuiTableColumnFlags_NoResize);
ImGui::TableSetupColumn("File", ImGuiTableColumnFlags_NoResize);
ImGui::TableHeadersRow();
auto& logs = GetLogHistory();
int32_t lineCount = bx::min(100, LogInternal::LogHistorySize);
for (int32_t i = 0; i < lineCount; ++i)
{
int32_t idx = logs.WriteIdx - i - 1;
if (idx < 0) idx += LogInternal::LogHistorySize;
const char* line = &logs.LogBuffer[idx * LogInternal::MaxLineSize];
if (line[0] != 0)
{
int64_t timeOffset = logs.WriteTime[idx] - time.StartTime;
double writeTime = (double)timeOffset / bx::getHPFrequency();
uint32_t fileLine = logs.LineBuffer[idx];
const char* filePath = &logs.FileBuffer[idx * LogInternal::MaxLineSize];
const char* filePathRes =
debug.ShortenLogFileNames ? bx::FilePath{filePath}.getFileName().getPtr() : filePath;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%.01f", writeTime);
ImGui::TableNextColumn();
ImGui::Text("%s", line);
ImGui::SetItemTooltip("%f\n%s%s:%u", writeTime, line, filePath, fileLine);
ImGui::TableNextColumn();
ImGui::Text("%u", fileLine);
ImGui::TableNextColumn();
ImGui::Text("%s", filePathRes);
if (i > 0) ImGui::PopStyleColor(2);
ImVec4 bgCol = {0.0f, 0.0f, 0.0f, 1.0f};
if (logs.WriteType[idx] == ELogType::Warn)
bgCol = {0.2f, 0.2f, 0.0f, 1.0f};
else if (logs.WriteType[idx] == ELogType::Error)
bgCol = {0.2f, 0.0f, 0.0f, 1.0f};
ImGui::PushStyleColor(ImGuiCol_TableRowBg, bgCol);
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, bgCol);
}
}
ImGui::EndTable();
if (lineCount > 0) ImGui::PopStyleColor(2);
}
ImGui::End();
}
void RenderRenderSettingsUI(Game::GameRendering& rendering)
{
auto& time = Game::GetInstance().Time;
auto& shared = Game::GetShared();
auto& debug = Game::GetInstance().DebugData;
auto& level = Game::GetInstance().GameLevel;
auto& player = Game::GetInstance().Player;
if (ImGui::Begin("Rendering"))
{
if (rendering.LastShaderLoadTime >= 0.0f)
{
ImGui::TextColored({0.2f, 0.9f, 0.2f, 1.0f},
"Shader loaded %.0f seconds ago",
time.Now - rendering.LastShaderLoadTime);
}
else
{
ImGui::TextColored({0.9f, 0.2f, 0.2f, 1.0f}, "Shader load Failiure!");
}
ImGui::SameLine();
if (ImGui::Button("Reload Level"))
{
level = {};
level.Setup(shared.Game);
}
ImGui::DragFloat3("Player Pos", &player.PlayerCamTransform.Position.x);
ImGui::DragFloat3("Puz0 Pos", &level.Puzzles[0].WorldPosition.x);
ImGui::SliderFloat("Mouse Sensitivity", &player.MouseSensitivity, 0.1f, 5.0f);
ImGui::SliderFloat("Player Speed", &player.MovementSpeed, 1.0f, 30.0f);
ImGui::Checkbox("Show ImGui Demo", &debug.ShowImguiDemo);
ImGui::Checkbox("Show Stats", &debug.ShowStats);
if (debug.ShowImguiDemo) ImGui::ShowDemoWindow(&debug.ShowImguiDemo);
ImGui::Separator();
if (ImGui::TreeNodeEx("Entity Groups"))
{
ImGui::Checkbox("Cubes", &level.Cubes.IsEnabled);
ImGui::Checkbox("Tests", &level.Tests.IsEnabled);
ImGui::Checkbox("PuzzleTiles", &level.PuzzleTiles.IsEnabled);
ImGui::Checkbox("UIQuads", &level.UIQuads.IsEnabled);
ImGui::Checkbox("Level", &level.LevelEntities.IsEnabled);
ImGui::TreePop();
}
bool uiconfigChanged = false;
if (ImGui::TreeNodeEx("Game Tablet"))
{
uiconfigChanged |= Tools::EntityDataSettings(player.Config.TabletBackgroundRenderData);
ImGui::Separator();
ImGui::Text("Status");
uiconfigChanged |= Tools::EntityDataSettings(player.Config.TabletStatusRenderData);
uiconfigChanged |= Tools::TextureDropdown(player.Config.TabletStatusSolvedTexture, "Solved Texture");
uiconfigChanged |=
Tools::TextureDropdown(player.Config.TabletStatusNotSolvedTexture, "Not Solved Texture");
ImGui::Separator();
ImGui::Text("Reset");
uiconfigChanged |= Tools::EntityDataSettings(player.Config.TabletResetRenderData);
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Background Level"))
{
for (int32_t i = 0; i < BX_COUNTOF(player.Config.BackgroundLevelRenderData); ++i)
{
if (i > 0) ImGui::Separator();
uiconfigChanged |= Tools::EntityDataSettings(player.Config.BackgroundLevelRenderData[i]);
}
ImGui::TreePop();
}
if (uiconfigChanged)
{
Serializer s;
s.Init("game/data/static/uiconfig.dat", "UICO");
s.WriteT(player.Config);
s.Finish();
level.PuzzleUI.Reset();
level.ReloadLevelEntities();
}
if (ImGui::TreeNodeEx("Dithering"))
{
if (ImGui::Button("Dithergen"))
{
DitherGen(rendering.DitherTextures, rendering.DitherRecursion);
}
ImGui::SameLine();
ImGui::SliderInt("Recursion", &rendering.DitherRecursion, 1, 4);
ImGui::Text("%ux%ux%u",
rendering.DitherTextures.DitherTexWH,
rendering.DitherTextures.DitherTexWH,
rendering.DitherTextures.DitherTexDepth);
if (!isValid(rendering.DitherTextures.PreviewTex))
{
ImGui::Text("Invalid Texture");
}
else
{
ImGui::Image(
rendering.DitherTextures.PreviewTex.idx,
{(float)rendering.DitherTextures.DitherTexWH,
(float)rendering.DitherTextures.DitherTexWH * rendering.DitherTextures.DitherTexDepth});
}
if (isValid(rendering.DitherTextures.RampTex))
{
ImGui::Image(rendering.DitherTextures.RampTex.idx,
{BX_COUNTOF(rendering.DitherTextures.BrightnessRamp), 8});
}
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Shader log"))
{
ImGui::TextWrapped("%s", Game::GetShared().Dev.ShaderLog);
ImGui::TreePop();
}
}
ImGui::End();
}
void RenderTexturesUI(Game::GameRendering& rendering)
{
if (ImGui::Begin("Textures"))
{
if (ImGui::Button("Reload"))
{
rendering.LoadTextures();
}
for (int32_t i = 0; i < rendering.MaxTextures; ++i)
{
if (!isValid(rendering.Textures[i].RenderHandle)) continue;
ImGui::Text("%i", i);
float width = bx::min<float>(ImGui::GetContentRegionAvail().x, rendering.Textures[i].Info.width);
float height = bx::min<float>(ImGui::GetContentRegionAvail().x, rendering.Textures[i].Info.height);
ImGui::Image(rendering.Textures[i].RenderHandle.idx, {width, height});
}
}
ImGui::End();
}
void RenderModelsUI(Game::GameRendering& rendering)
{
if (ImGui::Begin("Models"))
{
if (ImGui::Button("Reload"))
{
LoadModels(rendering.Models, rendering.ModelCount);
}
for (int32_t i = 0; i < rendering.ModelCount; ++i)
{
auto& mdl = rendering.Models[i];
if (bgfx::isValid(mdl.HeightMapTexture))
{
ImGui::Text("%s", mdl.Name);
ImGui::Image(mdl.HeightMapTexture.idx,
ImVec2{(float)Game::HeightMap::Height, (float)Game::HeightMap::Width});
}
else
{
ImGui::Text("Invalid Handle!");
}
ImGui::Spacing();
}
}
ImGui::End();
}
void RenderPuzzlesUI()
{
auto& debug = Game::GetInstance().DebugData;
auto& level = Game::GetInstance().GameLevel;
if (ImGui::Begin("Puzzles"))
{
char nameBuf[64]{0};
for (int32_t i = 0; i < BX_COUNTOF(level.Puzzles); ++i)
{
ImGui::PushID(i);
auto& puzzleData = level.Puzzles[i].Data;
bool isSelected = debug.SelectedDebugLevel == i;
ImGui::PushID("selectable");
bx::snprintf(nameBuf, sizeof(nameBuf), "%u: %s", i, puzzleData.PuzzleName);
if (ImGui::Selectable(nameBuf, isSelected))
{
debug.SelectedDebugLevel = isSelected ? UINT16_MAX : i;
}
ImGui::DragFloat3("Pos", &level.Puzzles[i].WorldPosition.x);
ImGui::PopID();
ImGui::PopID();
}
}
ImGui::End();
if (debug.SelectedDebugLevel < BX_COUNTOF(level.Puzzles))
{
if (!Puzzle::RenderDebugUI(level.Puzzles[debug.SelectedDebugLevel].Data))
{
debug.SelectedDebugLevel = UINT16_MAX;
}
}
}
void RenderCardsUI(Game::GameRendering& rendering)
{
auto& debug = Game::GetInstance().DebugData;
if (ImGui::Begin("Cards"))
{
Gen::StaticPuzzleData& staticData = Puzzle::GetStaticPuzzleData();
if (ImGui::Button("Save"))
{
Puzzle::SaveStaticPuzzleData();
}
ImGui::SameLine();
if (ImGui::Button("Reload"))
{
Puzzle::LoadStaticPuzzleData();
}
ImGui::Separator();
ImGui::ColorEdit3("Disabled Tint", &staticData.Visuals.DisabledCardTint.x);
ImGui::ColorEdit3("Tile Base Color", &staticData.Visuals.TileBaseColor.x);
ImGui::ColorEdit3("Tile Dot Color", &staticData.Visuals.TileDotColor.x);
for (int32_t i = 0; i < BX_COUNTOF(staticData.Cards); ++i)
{
ImGui::Separator();
Gen::StaticPuzzleCard& card = staticData.Cards[i];
ImGui::PushID(i);
char cardName[64]{0};
bx::snprintf(cardName, sizeof(cardName), "%i", i);
ImGui::Selectable(cardName);
if (ImGui::BeginDragDropSource())
{
Puzzle::DrawCard(card, debug.DebugCardRotation, ImGui::GetCursorScreenPos());
ImGui::SetDragDropPayload("cardtype", &i, sizeof(i));
ImGui::EndDragDropSource();
}
Tools::ModelDropdown(card.BaseModelHandle);
Tools::TextureDropdown(card.ModelTextureHandle, "World Texture");
Tools::TextureDropdown(card.BoardTextureHandle, "UI Texture");
if (IsValid(card.BaseModelHandle))
{
auto& mdl = rendering.Models[card.BaseModelHandle.ModelIdx];
if (mdl.SocketCount > 0 && ImGui::TreeNodeEx("Slots"))
{
for (int32_t sIdx = 0; sIdx < mdl.SocketCount; ++sIdx)
{
Tools::ModelDropdown(card.Sockets[sIdx].Model, mdl.Sockets[sIdx].Name);
int val = card.Sockets[sIdx].ConnectionDirection;
ImGui::PushID(sIdx);
if (ImGui::Combo("Connection Direction", &val, "North\0East\0South\0West\0"))
{
card.Sockets[sIdx].ConnectionDirection = val;
}
ImGui::PopID();
}
ImGui::TreePop();
}
}
ImGui::Text("Card");
for (int8_t y = 0; y < Puzzle::Config::CardSize; ++y)
{
ImGui::PushID(y);
for (int8_t x = 0; x < Puzzle::Config::CardSize; ++x)
{
if (x > 0) ImGui::SameLine();
ImGui::PushID(x);
auto& node = Puzzle::EditCardNodeAt(card, 0, x, y);
if (ImGui::Button(Gen::PuzzleElementType::ShortName[node], {26, 24}))
{
int32_t newVal = int32_t(node) + 1;
if (newVal >= Gen::PuzzleElementType::EntryCount)
{
newVal = 0;
}
node = Gen::PuzzleElementType::Enum(newVal);
}
ImGui::PopID();
}
ImGui::PopID();
}
ImGui::PopID();
}
}
ImGui::End();
}
void RenderEntitiesUI()
{
auto& level = Game::GetInstance().GameLevel;
if (ImGui::Begin("Entities"))
{
if (ImGui::TreeNodeEx("UIQuads"))
{
ImGui::Text("Count: %u", level.UIQuads.Count);
for (uint16_t i = 0; i < level.UIQuads.Count; ++i)
{
ImGui::Separator();
ImGui::PushID(i);
ImGui::Text("%u", i);
ImGui::SameLine();
auto& quad = level.UIQuads.Get({i});
ImGui::Checkbox("Visible", &quad.EData.Visible);
TextureDropdown(quad.EData.TextureHandle);
MaterialDropdown(quad.EData.MaterialHandle);
ImGui::DragFloat3("Pos", &quad.EData.Transform.Position.x);
ImGui::DragFloat3("UI Pos", &quad.UIPos.x);
ImGui::Text("RenderID: %i", quad.EData.RenderID);
ImGui::PopID();
}
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Level"))
{
ImGui::Text("Count: %u", level.LevelEntities.Count);
for (uint16_t i = 0; i < level.LevelEntities.Count; ++i)
{
ImGui::Separator();
ImGui::PushID(i);
ImGui::Text("%u", i);
ImGui::SameLine();
auto& levelEnt = level.LevelEntities.Get({i});
ImGui::Checkbox("Visible", &levelEnt.EData.Visible);
TextureDropdown(levelEnt.EData.TextureHandle);
MaterialDropdown(levelEnt.EData.MaterialHandle);
ImGui::DragFloat3("Pos", &levelEnt.EData.Transform.Position.x);
ImGui::Text("RenderID: %i", levelEnt.EData.RenderID);
ImGui::PopID();
}
ImGui::TreePop();
}
}
ImGui::End();
}
void RenderStatsUI()
{
auto& time = Game::GetInstance().Time;
auto& debug = Game::GetInstance().DebugData;
if (debug.ShowStats)
{
ImGui::SetNextWindowPos({0, 0});
if (ImGui::Begin("Stats",
nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoInputs |
ImGuiWindowFlags_AlwaysAutoResize))
{
if (Game::GetInstance().Player.CameraM == Game::CameraMode::Freefly)
{
ImGui::PushStyleColor(ImGuiCol_Text, {0.8f, 0.1f, 0.1f, 1.0f});
ImGui::Text("NOCLIP");
ImGui::PopStyleColor();
}
ImGui::Text("Delta: %.01fms", time.Delta * 1000);
ImGui::Text("FPS: %.0f", 1.0 / time.Delta);
constexpr ImVec2 FpsPlotSize{200, 60};
if (ImGui::BeginChild("FpsPlot", FpsPlotSize, 0, ImGuiWindowFlags_NoInputs))
{
auto& drawList = *ImGui::GetWindowDrawList();
ImVec2 pos = ImGui::GetWindowPos();
drawList.AddRectFilled(pos, {pos.x + FpsPlotSize.x, pos.y + FpsPlotSize.y}, 0x22222233);
float scale = 1000.0f;
for (int32_t i = 0; i < FrameTimeBufSize; ++i)
{
int32_t idx = FrameTimeIdx - i - 1;
int32_t prevIdx = idx - 1;
if (idx < 0) idx += FrameTimeBufSize;
if (prevIdx < 0) prevIdx += FrameTimeBufSize;
if (FrameTimes[idx] == 0 || FrameTimes[prevIdx] == 0) continue;
int64_t frameTime = FrameTimes[idx] - FrameTimes[prevIdx];
double frameTimeSec = (double)frameTime / bx::getHPFrequency();
drawList.AddLine(
{pos.x + (FpsPlotSize.x - i - 1), pos.y + FpsPlotSize.y},
{pos.x + (FpsPlotSize.x - i - 1), pos.y + FpsPlotSize.y - (float)frameTimeSec * scale},
0xFFFFFFFF);
}
}
ImGui::EndChild();
}
ImGui::End();
}
}
void RenderDebugUI(Game::GameRendering& rendering)
{
if (!rendering.SetupData.UseImgui) return;
ZoneScopedN("DebugUI");
auto& time = Game::GetInstance().Time;
auto& shared = Game::GetShared();
auto& debug = Game::GetInstance().DebugData;
auto& level = Game::GetInstance().GameLevel;
auto& player = Game::GetInstance().Player;
if (ImGui::Begin("Hilfe"))
{
if (ImGui::Button("Spiel Neustarten"))
{
player.PlayerCamTransform.Position = {0.0f, 3.0f, 0.0f};
for (int32_t i = 0; i < BX_COUNTOF(level.Puzzles); ++i)
{
Puzzle::ResetPuzzle(level.Puzzles[i].Data);
}
}
ImGui::SameLine();
if (ImGui::Button("Zurück zum Anfang"))
{
player.PlayerCamTransform.Position = {0.0f, 3.0f, 0.0f};
}
ImGui::Separator();
ImGui::Text("Anleitung:");
ImGui::Text("Bewege dich mit");
ImGui::SameLine();
ImGui::TextColored({1.0f, 0.6f, 0.6f, 1.0f}, "W, A, S, D");
ImGui::SameLine();
ImGui::Text("und schau dich um mit der");
ImGui::SameLine();
ImGui::TextColored({1.0f, 0.6f, 0.6f, 1.0f}, "Maus.");
ImGui::Text("Drücke");
ImGui::SameLine();
ImGui::TextColored({1.0f, 0.6f, 0.6f, 1.0f}, "Leertaste");
ImGui::SameLine();
ImGui::Text("um den Spielplan zu öffnen.");
ImGui::Text("Auf dem Spielplan kannst du Karten verschieben.");
ImGui::Text("Mit");
ImGui::SameLine();
ImGui::TextColored({1.0f, 0.6f, 0.6f, 1.0f}, "Rechter Maustaste");
ImGui::SameLine();
ImGui::Text("kannst du Karten drehen.");
ImGui::Text("");
ImGui::Text("Dein Ziel: Verbinde die Pumpe mit dem Abfluss, um das");
ImGui::Text("Tor zum nächsten Level zu öffnen!");
ImGui::Text("");
auto& inflowTexture = rendering.Textures[10];
auto& outflowTexture = rendering.Textures[9];
auto& connectionTexture = rendering.Textures[8];
ImGui::Text("Pumpe (Wasserquelle):");
ImGui::Image(inflowTexture.RenderHandle.idx, {64.0f, 64.0f});
ImGui::Text("Abfluss (Ziel):");
ImGui::Image(outflowTexture.RenderHandle.idx, {64.0f, 64.0f});
ImGui::Text("Verbindung:");
ImGui::Image(connectionTexture.RenderHandle.idx, {64.0f, 64.0f});
}
ImGui::End();
if (rendering.UIVisible == Game::UIVisibilityState::Debug)
{
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
{
debug.DebugCardRotation++;
if (debug.DebugCardRotation >= 4) debug.DebugCardRotation = 0;
}
if (ImGui::Begin("Debug"))
{
ImGui::Checkbox("ImGui Demo", &debug.ShowImguiDemo);
ImGui::SliderFloat("Font Scale", &ImGui::GetIO().FontGlobalScale, 0.5f, 4.0f);
ImGui::Checkbox("Break on ID", &debug.DebugBreakIDEnabled);
ImGui::SameLine();
ImGui::PushID("@#$");
ImGui::InputInt("", &debug.DebugBreakID);
ImGui::PopID();
ImGui::Separator();
ImGui::Text("Arenas");
ProgressBar("Permanent", shared.Game.PermanentArena.Used, shared.Game.PermanentArena.MaxSize);
ProgressBar("Entity", shared.Game.EntityArena.Used, shared.Game.EntityArena.MaxSize);
ProgressBar("Transient", shared.Game.TransientArena.Used, shared.Game.TransientArena.MaxSize);
}
ImGui::End();
RenderLogUI();
RenderRenderSettingsUI(rendering);
RenderTexturesUI(rendering);
RenderModelsUI(rendering);
RenderPuzzlesUI();
RenderCardsUI(rendering);
RenderEntitiesUI();
}
RenderStatsUI();
}
void MeasureFrameEnd()
{
FrameTimes[FrameTimeIdx] = bx::getHPCounter();
++FrameTimeIdx;
if (FrameTimeIdx >= FrameTimeBufSize) FrameTimeIdx = 0;
}
} // namespace Tools

View File

@@ -1,7 +1,14 @@
#pragma once
#include <cstdint>
#include "../gen/Generated.h"
#include "rendering/Rendering.h"
namespace Tools
{
void ModelDropdown(uint16_t& modelHandle);
bool EntityDataSettings(Gen::SavedEntityRenderData& data);
bool ModelDropdown(Gen::ModelHandle& modelHandle, const char* title = "Model");
bool TextureDropdown(Gen::TextureHandle& texHandle, const char* title = "Texture");
bool MaterialDropdown(Gen::EMaterial::Enum& material);
bool TransformUI(Gen::Transform& transform);
void RenderDebugUI(Game::GameRendering& rendering);
void MeasureFrameEnd();
} // namespace Tools

382
src/game/UI.cpp Normal file
View File

@@ -0,0 +1,382 @@
#include "UI.h"
#include "Entity.h"
#include "Gen.h"
#include "Global.h"
#include "Input.h"
#include "Instance.h"
#include "Level.h"
#include "Puzzle.h"
#include "bx/math.h"
using namespace Gen;
namespace
{
Game::StaticUIData StaticData;
}
namespace Game
{
UIQuadEntityHandle NewQuad(UIQuadEntityManager& manager,
const Gen::SavedEntityRenderData& loadData,
UIQuadEntityHandle oldHandle)
{
UIQuadEntityHandle h = oldHandle;
if (!IsValid(h))
{
h = manager.New();
if (!IsValid(h)) return h;
}
UIQuadEntity& entity = manager.Get(h);
entity.EData.LoadFromSaved(loadData);
entity.UIPos = entity.EData.Transform.Position;
return h;
}
void UpdateQuad(UIQuadEntityManager& manager, UIQuadEntityHandle handle)
{
if (!IsValid(handle)) return;
UIQuadEntity& entity = manager.Get(handle);
entity.EData.Transform.Position = StaticData.UITransform.Position;
entity.EData.Transform.Rotation = StaticData.UITransform.Rotation;
TranslateLocal(entity.EData.Transform, entity.UIPos);
Rotate(entity.EData.Transform, Vec3{bx::kPi * 0.5f, 0.0f, (1.0f - 0 * 0.5f) * bx::kPi});
Rotate(entity.EData.Transform, {0.0f, entity.UIRot, 0.0f});
}
Vec3 GetMousePosWorld()
{
auto& window = GetShared().Window;
Vec2 mousePos = GetMousePos();
mousePos.x = mousePos.x / window.WindowWidth;
mousePos.y = mousePos.y / window.WindowHeight;
mousePos *= 2.0f;
mousePos -= 1.0f;
Vec4 mousePosView = {mousePos.x, -mousePos.y, 0.0f, 1.0f};
Vec4 mousePosCam4 = Mul(GetInstance().Player.ProjectionInverse, mousePosView);
Vec3 mousePosCam = Vec3{
mousePosCam4.x /= mousePosCam4.w,
mousePosCam4.y /= mousePosCam4.w,
mousePosCam4.z /= mousePosCam4.w,
};
return LocalToGlobalPoint(GetInstance().Player.PlayerCamTransform, mousePosCam);
}
bool IsQuadHovered(Transform& quadTransform, Vec3 mousePosWorld, Vec3& outQuadPlaneIntersectPos)
{
Vec3 quadPosWorld = quadTransform.Position;
Vec3 quadXWorld = LocalToGlobalPoint(quadTransform, {1, 0, 0});
Vec3 quadZWorld = LocalToGlobalPoint(quadTransform, {0, 0, 1});
if (RayPlaneIntersect(GetInstance().Player.PlayerCamTransform.Position,
mousePosWorld,
quadPosWorld,
quadXWorld,
quadZWorld,
outQuadPlaneIntersectPos))
{
Vec3 quadSpaceIntersect = GlobalToLocalPoint(quadTransform, outQuadPlaneIntersectPos);
if (quadSpaceIntersect.x >= -1.0f && quadSpaceIntersect.x <= 1.0f && quadSpaceIntersect.z >= -1.0f &&
quadSpaceIntersect.z <= 1.0f)
{
return true;
}
}
return false;
}
void WorldPuzzleUI::Setup()
{
auto& level = GetInstance().GameLevel;
auto& player = GetInstance().Player;
TabletHandle = NewQuad(level.UIQuads, player.Config.TabletBackgroundRenderData);
SolvedQuad = NewQuad(level.UIQuads, player.Config.TabletStatusRenderData);
ResetQuad = NewQuad(level.UIQuads, player.Config.TabletResetRenderData);
for (int32_t i = 0; i < Puzzle::Config::MaxCardsInPuzzle; ++i)
{
UIPlacedCards[i] = level.UIQuads.New();
auto& quad = level.UIQuads.Get(UIPlacedCards[i]);
quad.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/plane.glb");
quad.EData.MaterialHandle = EMaterial::UI;
}
for (int32_t i = 0; i < Puzzle::Config::MaxAvailableStacks * UIAvailableCardMaxStackPreview; ++i)
{
UIAvailableCards[i] = level.UIQuads.New();
auto& quad = level.UIQuads.Get(UIAvailableCards[i]);
quad.EData.MaterialHandle = EMaterial::UI;
quad.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/plane.glb");
}
}
Vec3 CalcBoardOffset(const Gen::PuzzleData& puzData)
{
return Vec3{
(float)(puzData.WidthTiles) / 2.0f - 0.5f,
(float)(puzData.HeightTiles) / 2.0f - 0.5f,
0.0f,
} *
WorldPuzzleUI::UICardScale * WorldPuzzleUI::UICardScale;
}
Vec3 CardPosToUIPos(const Gen::PuzzleData& data, int32_t cardX, int32_t cardY)
{
return Vec3{(float)cardX, (float)(data.HeightTiles / 2 - cardY - 1), -0.3f} * WorldPuzzleUI::UICardOffset *
WorldPuzzleUI::UICardScale -
CalcBoardOffset(data);
}
void UIPosToCardPos(const Gen::PuzzleData& data, Vec3 uiPos, int32_t& cardXOut, int32_t& cardYOut)
{
Vec3 boardOffset = CalcBoardOffset(data) / WorldPuzzleUI::UICardScale;
Vec3 boardPos = GlobalToLocalPoint(StaticData.UITransform, uiPos);
Vec3 boardTilePos = (boardPos + boardOffset) / WorldPuzzleUI::UICardOffset;
cardXOut = (int32_t)bx::round(boardTilePos.x);
cardYOut = data.HeightTiles / 2 - (int32_t)bx::round(boardTilePos.y) - 1;
}
void WorldPuzzleUI::UpdateAvailableCards(Gen::PuzzleData& Data)
{
auto& level = GetInstance().GameLevel;
auto& staticCards = Puzzle::GetStaticPuzzleData().Cards;
Vec3 quadPlaneIntersectPos;
Vec3 mousePosWorld = GetMousePosWorld();
for (int32_t stackIdx = 0; stackIdx < Puzzle::Config::MaxAvailableStacks; ++stackIdx)
{
auto& card = Data.AvailableCards[stackIdx];
for (int32_t cardIdx = 0; cardIdx < UIAvailableCardMaxStackPreview; cardIdx++)
{
auto h = UIAvailableCards[stackIdx * UIAvailableCardMaxStackPreview + cardIdx];
auto& quad = level.UIQuads.Get(h);
int32_t remaining = (int32_t)card.MaxAvailableCount - (int32_t)card.UsedCount;
if (stackIdx < Data.AvailableCardCount && cardIdx < remaining)
{
quad.UIPos = Vec3{cardIdx * 0.05f + stackIdx * 1.2f,
4.2f + (cardIdx % 2 == 0 ? 0.02f : 0.0f),
cardIdx * 0.001f - 0.3f} *
UICardOffset * UICardScale;
UpdateQuad(level.UIQuads, h);
quad.EData.Visible = true;
quad.EData.TextureHandle = Puzzle::IsValid(card.RefCard)
? staticCards[card.RefCard.Idx].BoardTextureHandle
: Gen::TextureHandle{0};
quad.EData.Transform.Scale = {UICardScale, UICardScale, UICardScale};
quad.EData.DotColor = {1.0f, 1.0f, 1.0f, 1.0f};
quad.EData.BaseColor = {1.0f, 1.0f, 1.0f, 1.0f};
if (cardIdx == 0)
{
if (IsQuadHovered(quad.EData.Transform, mousePosWorld, quadPlaneIntersectPos) &&
DraggedAvailableCardIdx == UINT16_MAX && DraggedCard.X == -1 &&
GetMouseButtonPressedNow(MouseButton::Left))
{
DraggedAvailableCardIdx = stackIdx;
}
if (DraggedAvailableCardIdx == stackIdx)
{
Vec3 dragPos = quadPlaneIntersectPos;
dragPos += StaticData.ZAxis * -0.01f;
quad.EData.Transform.Position = dragPos;
int32_t xPos;
int32_t yPos;
UIPosToCardPos(Data, quadPlaneIntersectPos, xPos, yPos);
if (!GetMouseButton(MouseButton::Left))
{
if (xPos >= 0 && xPos < Data.WidthTiles / Puzzle::Config::CardSize && yPos >= 0 &&
yPos < Data.HeightTiles / Puzzle::Config::CardSize)
{
Gen::PuzPos targetCardPos = {(int8_t)xPos, (int8_t)yPos};
Gen::PlacedPuzzleCard& targetCard =
Data.PlacedCards[yPos * Puzzle::Config::MaxPuzzleSizeCards + xPos];
if (!Puzzle::IsValid(targetCard.RefCard) || targetCard.RefCard.Idx == 0)
{
Puzzle::DragAvailableCardTo(Data, targetCardPos, DraggedAvailableCardIdx, 0);
}
}
DraggedAvailableCardIdx = UINT16_MAX;
}
}
}
}
else
{
quad.EData.Visible = false;
}
}
}
}
void WorldPuzzleUI::UpdateBoardCards(Gen::PuzzleData& Data)
{
auto& level = GetInstance().GameLevel;
auto& staticCards = Puzzle::GetStaticPuzzleData().Cards;
Vec3 quadPlaneIntersectPos;
Vec3 boardOffset = CalcBoardOffset(Data);
for (int8_t y = 0; y < Data.HeightTiles / Puzzle::Config::CardSize; ++y)
{
for (int8_t x = 0; x < Data.WidthTiles / Puzzle::Config::CardSize; ++x)
{
// UI Quad
int32_t cardIdx = y * Puzzle::Config::MaxPuzzleSizeCards + x;
PlacedPuzzleCard& card = Data.PlacedCards[cardIdx];
bool isValid = Puzzle::IsValid(card.RefCard);
bool isLocked = GetFlag(card.Flags, PlacedPuzzleCardFlags::Locked);
auto& quad = level.UIQuads.Get(UIPlacedCards[cardIdx]);
quad.UIPos = CardPosToUIPos(Data, card.Position.X, card.Position.Y);
quad.UIRot = card.Rotation * bx::kPi * -0.5f;
UpdateQuad(level.UIQuads, UIPlacedCards[cardIdx]);
quad.EData.Visible = isValid;
quad.EData.TextureHandle =
isValid ? staticCards[card.RefCard.Idx].BoardTextureHandle : Gen::TextureHandle{};
quad.EData.DotColor =
isLocked ? Puzzle::GetStaticPuzzleData().Visuals.DisabledCardTint : Vec4{1.0f, 1.0f, 1.0f, 1.0f};
quad.EData.Transform.Scale = {UICardScale, UICardScale, UICardScale};
if (isValid && IsQuadHovered(quad.EData.Transform, StaticData.MousePosWorld, quadPlaneIntersectPos))
{
if (!isLocked && DraggedCard.X == -1 && DraggedAvailableCardIdx == UINT16_MAX)
{
if (GetMouseButtonPressedNow(MouseButton::Left))
{
DraggedCard.X = x;
DraggedCard.Y = y;
}
if (GetMouseButtonPressedNow(MouseButton::Right))
{
Puzzle::RotateCard(card);
}
}
}
if (DraggedCard.X == x && DraggedCard.Y == y)
{
Vec3 dragPos = quadPlaneIntersectPos;
dragPos += StaticData.ZAxis * -0.01f;
quad.EData.Transform.Position = dragPos;
int32_t xPos;
int32_t yPos;
UIPosToCardPos(Data, quadPlaneIntersectPos, xPos, yPos);
Gen::PuzPos srcCardPos = {(int8_t)DraggedCard.X, (int8_t)DraggedCard.Y};
Gen::PlacedPuzzleCard& srcCard =
Data.PlacedCards[srcCardPos.Y * Puzzle::Config::MaxPuzzleSizeCards + srcCardPos.X];
if (GetMouseButtonPressedNow(MouseButton::Right))
{
Puzzle::RotateCard(srcCard);
}
if (!GetMouseButton(MouseButton::Left))
{
Gen::PuzPos targetCardPos = {(int8_t)xPos, (int8_t)yPos};
if (xPos >= 0 && xPos < Data.WidthTiles / Puzzle::Config::CardSize && yPos >= 0 &&
yPos < Data.HeightTiles / Puzzle::Config::CardSize)
{
PlacedPuzzleCard srcCardCopy = srcCard;
Gen::PlacedPuzzleCard& targetCard =
Data.PlacedCards[yPos * Puzzle::Config::MaxPuzzleSizeCards + xPos];
bool canBeReplaced = !Puzzle::IsValid(targetCard.RefCard) || targetCard.RefCard.Idx == 0;
if (canBeReplaced && Puzzle::ReturnPlacedCard(Data, srcCardPos))
{
int32_t foundIdx = -1;
for (int32_t availCardIdx = 0; availCardIdx < Data.AvailableCardCount; ++availCardIdx)
{
if (Data.AvailableCards[availCardIdx].RefCard.Idx == srcCardCopy.RefCard.Idx)
{
foundIdx = availCardIdx;
break;
}
}
if (foundIdx >= 0)
{
Puzzle::DragAvailableCardTo(Data, targetCardPos, foundIdx, srcCard.Rotation);
}
else
{
LOG_ERROR("NOTFOUND: %u", srcCardCopy.RefCard.Idx);
}
}
}
else
{
Puzzle::ReturnPlacedCard(Data, srcCardPos);
}
DraggedCard.X = -1;
DraggedCard.Y = -1;
}
}
}
}
}
void WorldPuzzleUI::Update(Gen::PuzzleData& Data, bool IsPuzzleSolved)
{
auto& level = GetInstance().GameLevel;
auto& player = GetInstance().Player;
Transform& camTransform = player.PlayerCamTransform;
UpdateMatrix(camTransform);
// UI Tablet
if (IsValid(TabletHandle))
{
auto& tablet = level.UIQuads.Get(TabletHandle);
tablet.EData.Transform.Rotation = camTransform.Rotation;
Rotate(tablet.EData.Transform, {0.5f * bx::kPi, 0.0f, 0.0f});
tablet.EData.Transform.Position = camTransform.Position + AxisForward(camTransform.M) * 1.0f;
StaticData.UITransform = tablet.EData.Transform;
tablet.EData.Transform.Position += {0.0f, 0.0f, 0.01f};
}
StaticData.UITransform.Rotation = camTransform.Rotation;
StaticData.ZAxis = AxisForward(StaticData.UITransform.M);
StaticData.UITransform.Position += StaticData.ZAxis * -0.01f;
StaticData.MousePosWorld = GetMousePosWorld();
// NOLINTBEGIN
Vec2 uiOffset = Vec2{static_cast<float>(Data.WidthTiles / Puzzle::Config::CardSize - 1),
static_cast<float>(Data.HeightTiles / Puzzle::Config::CardSize - 1)};
// NOLINTEND
uiOffset *= -UICardOffset * 0.5f;
auto& solvedQuad = level.UIQuads.Get(SolvedQuad);
solvedQuad.EData.Visible = true;
solvedQuad.EData.TextureHandle = IsPuzzleSolved ? GetInstance().Player.Config.TabletStatusSolvedTexture
: GetInstance().Player.Config.TabletStatusNotSolvedTexture;
UpdateQuad(level.UIQuads, SolvedQuad);
auto& resetQuad = level.UIQuads.Get(ResetQuad);
resetQuad.EData.Visible = true;
UpdateQuad(level.UIQuads, ResetQuad);
Vec3 hoverPosWorld;
if (GetMouseButtonPressedNow(MouseButton::Left) &&
IsQuadHovered(resetQuad.EData.Transform, StaticData.MousePosWorld, hoverPosWorld))
{
Puzzle::ResetPuzzle(Data);
}
UpdateAvailableCards(Data);
UpdateBoardCards(Data);
}
void WorldPuzzleUI::Reset()
{
auto& level = GetInstance().GameLevel;
auto& config = GetInstance().Player.Config;
TabletHandle = NewQuad(level.UIQuads, config.TabletBackgroundRenderData, TabletHandle);
SolvedQuad = NewQuad(level.UIQuads, config.TabletStatusRenderData, SolvedQuad);
ResetQuad = NewQuad(level.UIQuads, config.TabletResetRenderData, ResetQuad);
}
} // namespace Game

44
src/game/UI.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include "../gen/Generated.h"
#include "Entity.h"
#include "Puzzle.h"
namespace Game
{
struct StaticUIData
{
Gen::Transform UITransform;
Gen::Vec3 ZAxis;
Gen::Vec3 MousePosWorld;
};
struct WorldPuzzleUI
{
static constexpr float UICardScale = 0.05f;
static constexpr float UICardOffset = 2.1f * UICardScale;
static constexpr int32_t UIAvailableCardMaxStackPreview = 3;
UIQuadEntityHandle TabletHandle;
UIQuadEntityHandle SolvedQuad;
UIQuadEntityHandle ResetQuad;
UIQuadEntityHandle UIPlacedCards[Puzzle::Config::MaxCardsInPuzzle];
UIQuadEntityHandle UIAvailableCards[Puzzle::Config::MaxAvailableStacks * UIAvailableCardMaxStackPreview];
Gen::PuzPos DraggedCard{-1, -1};
uint16_t DraggedAvailableCardIdx = UINT16_MAX;
void Setup();
void UpdateAvailableCards(Gen::PuzzleData& Data);
void UpdateBoardCards(Gen::PuzzleData& Data);
void Update(Gen::PuzzleData& Data, bool IsPuzzleSolved);
void Reset();
};
UIQuadEntityHandle NewQuad(UIQuadEntityManager& manager,
const Gen::SavedEntityRenderData& loadData,
UIQuadEntityHandle oldHandle = {});
void UpdateQuad(UIQuadEntityManager& manager, UIQuadEntityHandle handle);
Gen::Vec3 GetMousePosWorld();
bool IsQuadHovered(Gen::Transform& quadTransform, Gen::Vec3 mousePosWorld, Gen::Vec3& outQuadPlaneIntersectPos);
} // namespace Game

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/game/data/puzzles/1.pzl LFS Normal file

Binary file not shown.

BIN
src/game/data/puzzles/2.pzl LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,22 +1,22 @@
type Vec2
{
f32 X
f32 Y
f32 x
f32 y
}
type Vec3
{
f32 X
f32 Y
f32 Z
f32 x
f32 y
f32 z
}
type Vec4
{
f32 X
f32 Y
f32 Z
f32 W
f32 x
f32 y
f32 z
f32 w
}
type Mat3
@@ -38,6 +38,32 @@ type Mat4
}")
}
type Transform
{
Mat4 M
Mat4 MI
Vec3 Position
Mat4 Rotation
Vec3 Scale Default("{1.0f, 1.0f, 1.0f}")
}
type AssetHandle
{
u32 Idx Default("UINT32_MAX")
}
type ModelHandle
{
u16 ModelIdx Default("UINT16_MAX")
AssetHandle Asset
}
type TextureHandle
{
u16 TextureIdx Default("UINT16_MAX")
AssetHandle Asset
}
type PuzPos
{
i8 X
@@ -48,18 +74,31 @@ enum PuzzleElementType(u8)
{
None GameName("Empty") ShortName(" ")
WaterIn GameName("Water Source") ShortName("~+")
WaterGoal GameName("Water Goal") ShortName("~-")
WaterGoal GameName("Water Goal") ShortName("~!")
WaterChannel GameName("Water Channel") ShortName("~")
ElectricIn GameName("Electricity Source") ShortName("e+")
ElectricGoal GameName("Electricity Goal") ShortName("e-")
ElectricGoal GameName("Electricity Goal") ShortName("e!")
Blocked GameName("Blocked") ShortName("B")
Bridge GameName("Bridge") ShortName("#")
}
type CardSocket
{
ModelHandle Model
u8 ConnectionDirection
}
type StaticPuzzleCard
{
PuzzleElementType Elements Arr(4)
u16 ModelHandle
ModelHandle BaseModelHandle
ModelHandle NorthCoverHandle
ModelHandle EastCoverHandle
ModelHandle SouthCoverHandle
ModelHandle WestCoverHandle
CardSocket Sockets Arr(16)
TextureHandle ModelTextureHandle
TextureHandle BoardTextureHandle
}
type StaticPuzzleCardHandle
@@ -67,9 +106,18 @@ type StaticPuzzleCardHandle
u16 Idx Default("UINT16_MAX")
}
type PuzzleVisualSettings
{
Vec4 TileBaseColor
Vec4 TileDotColor
Vec3 Test
Vec4 DisabledCardTint
}
type StaticPuzzleData
{
StaticPuzzleCard Cards Arr(64)
PuzzleVisualSettings Visuals
}
type PuzzleCardStack
@@ -79,12 +127,18 @@ type PuzzleCardStack
u8 UsedCount
}
enum PlacedPuzzleCardFlags(u8) Flags
{
None
Locked
}
type PlacedPuzzleCard
{
StaticPuzzleCardHandle RefCard
PuzPos Position
u8 Rotation
b IsLocked
PlacedPuzzleCardFlags Flags
}
type PuzzleData
@@ -95,9 +149,36 @@ type PuzzleData
u8 HeightTiles
u32 AvailableCardCount
PuzzleCardStack AvailableCards Arr(16)
u32 PlacedCardCount
PlacedPuzzleCard PlacedCards Arr(256)
PlacedPuzzleCard InitialPlacedCards Arr(256)
PuzzleElementType BackgroundTiles Arr(1024)
u32 GoalPositionCount
PuzPos GoalPositions Arr(16)
}
enum EMaterial
{
Default
UI
}
type SavedEntityRenderData
{
Vec4 BaseColor
Vec4 HighlightColor
Transform TF
EMaterial Material
TextureHandle Texture
ModelHandle Model
b Visible
}
type SavedPlayerConfig
{
SavedEntityRenderData TabletBackgroundRenderData
SavedEntityRenderData TabletStatusRenderData
TextureHandle TabletStatusNotSolvedTexture
TextureHandle TabletStatusSolvedTexture
SavedEntityRenderData TabletResetRenderData
SavedEntityRenderData BackgroundLevelRenderData Arr(16)
}

View File

@@ -0,0 +1,134 @@
#include "../Gen.h"
#include "../Log.h"
#include "Dither.h"
#include "bx/math.h"
using namespace Gen;
void DitherGen(DitherData& data, int32_t recursion)
{
data.Points[0] = {0.0f, 0.0f};
data.Points[1] = {0.5f, 0.5f};
data.Points[2] = {0.5f, 0.0f};
data.Points[3] = {0.0f, 0.5f};
data.PointCount = 4;
for (int32_t i = 0; i < data.BrightnessBucketCount; ++i)
data.BrightnessBuckets[i] = 0;
// add "subdivided" beyer matrix layers
for (int32_t recursionLevel = 0; recursionLevel < recursion - 1; ++recursionLevel)
{
int32_t startCount = data.PointCount;
float offset = bx::pow(0.5f, recursionLevel + 1);
for (int32_t i = 1; i < 4; ++i)
{
for (int32_t j = 0; j < startCount; ++j)
{
data.Points[data.PointCount] = data.Points[j] + data.Points[i] * offset;
data.PointCount++;
}
}
}
// Texture info setup
uint64_t dotsPerSide = bx::round(bx::pow(2, recursion));
data.DitherTexDepth = dotsPerSide * dotsPerSide;
data.DitherTexWH = 16 * dotsPerSide;
uint64_t texPixelCount = data.DitherTexWH * data.DitherTexWH * data.DitherTexDepth;
if (BX_COUNTOF(DitherData::DitherTex) < texPixelCount)
{
LOG_ERROR("Too many pixels: %llu", texPixelCount);
return;
}
// What does this do?
float invRes = 1.0f / data.DitherTexWH;
for (int32_t z = 0; z < data.DitherTexDepth; ++z)
{
int32_t dotCount = z + 1;
float dotArea = 0.5f / dotCount;
float dotRadius = bx::sqrt(dotArea / bx::kPi);
int32_t zOffset = z * data.DitherTexWH * data.DitherTexWH;
for (int32_t y = 0; y < data.DitherTexWH; ++y)
{
int32_t yOffset = y * data.DitherTexWH;
for (int32_t x = 0; x < data.DitherTexWH; ++x)
{
Vec2 point{(x + 0.5f) * invRes, (y + 0.5f) * invRes};
float dist = bx::kFloatInfinity;
for (int32_t i = 0; i < dotCount; ++i)
{
Vec2 vec = point - data.Points[i];
float wrapX = bx::wrap(vec.x + 0.5f, 1.0f) - 0.5f;
float wrapY = bx::wrap(vec.y + 0.5f, 1.0f) - 0.5f;
Vec2 wrappedVec = {wrapX, wrapY};
float curDist = Magnitude(wrappedVec);
dist = bx::min(dist, curDist);
}
dist = dist / (dotRadius * 2.4f);
float val = bx::clamp(1.0f - dist, 0.0f, 1.0f);
data.DitherTex[x + yOffset + zOffset] = Vec4{val, val, val, 1.0f};
int32_t bucket = bx::clamp(
uint32_t(val * DitherData::BrightnessBucketCount), 0, DitherData::BrightnessBucketCount - 1);
data.BrightnessBuckets[bucket] += 1;
}
}
}
// Brightness ramp
int32_t sum = 0;
for (int32_t i = 0; i < data.BrightnessBucketCount; ++i)
{
sum += data.BrightnessBuckets[data.BrightnessBucketCount - 1 - i];
data.BrightnessRamp[i + 1] = sum / (float)texPixelCount;
}
// Upload textures
CleanupDitherData(data);
const bgfx::Memory* memPreview = bgfx::makeRef(data.DitherTex, texPixelCount * sizeof(Vec4));
const bgfx::Memory* memFinal = bgfx::makeRef(data.DitherTex, texPixelCount * sizeof(Vec4));
const bgfx::Memory* memRamp = bgfx::makeRef(data.BrightnessRamp, sizeof(data.BrightnessRamp));
data.PreviewTex = bgfx::createTexture2D(data.DitherTexWH,
data.DitherTexWH * data.DitherTexDepth,
false,
false,
bgfx::TextureFormat::RGBA32F,
0,
memPreview);
data.FinalTex = bgfx::createTexture3D(
data.DitherTexWH, data.DitherTexWH, data.DitherTexDepth, false, bgfx::TextureFormat::RGBA32F, 0, memFinal);
data.RampTex =
bgfx::createTexture2D(BX_COUNTOF(data.BrightnessRamp), 1, false, 1, bgfx::TextureFormat::R32F, 0, memRamp);
if (!isValid(data.DitherSampler))
{
data.DitherSampler = bgfx::createUniform("s_ditherSampler", bgfx::UniformType::Sampler);
}
if (!isValid(data.RampSampler))
{
data.RampSampler = bgfx::createUniform("s_rampSampler", bgfx::UniformType::Sampler);
}
}
void CleanupDitherData(DitherData& data)
{
if (isValid(data.PreviewTex))
{
bgfx::destroy(data.PreviewTex);
data.PreviewTex = BGFX_INVALID_HANDLE;
}
if (isValid(data.FinalTex))
{
bgfx::destroy(data.FinalTex);
data.FinalTex = BGFX_INVALID_HANDLE;
}
if (isValid(data.RampTex))
{
bgfx::destroy(data.RampTex);
data.RampTex = BGFX_INVALID_HANDLE;
}
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "../../gen/Generated.h"
#include <bgfx/bgfx.h>
#include <imgui.h>
struct DitherData
{
static constexpr uint32_t BrightnessBucketCount = 256;
Gen::Vec2 Points[4096];
uint32_t PointCount = 0;
Gen::Vec4 DitherTex[256 * 256 * 64];
uint32_t DitherTexWH = 0;
uint32_t DitherTexDepth = 0;
int32_t BrightnessBuckets[BrightnessBucketCount];
float BrightnessRamp[BrightnessBucketCount + 1];
bgfx::TextureHandle PreviewTex = BGFX_INVALID_HANDLE;
bgfx::TextureHandle FinalTex = BGFX_INVALID_HANDLE;
bgfx::TextureHandle RampTex = BGFX_INVALID_HANDLE;
bgfx::UniformHandle DitherSampler = BGFX_INVALID_HANDLE;
bgfx::UniformHandle RampSampler = BGFX_INVALID_HANDLE;
ImTextureID PreviewID = 0;
};
void DitherGen(DitherData& data, int32_t recursion);
void CleanupDitherData(DitherData& data);

View File

@@ -6,13 +6,13 @@
#include "../Tools.h"
#include "Rendering.h"
#include "SDL3/SDL_events.h"
#include "Dither.h"
#include "SDL3/SDL_events.h" // IWYU pragma: keep
#include "backends/imgui_impl_sdl3.h"
#include "bgfx/defines.h"
#include "bx/bx.h"
#include "bx/constants.h"
#include "bx/debug.h"
#include "bx/filepath.h"
#include "bx/math.h"
#include "bx/string.h"
#include "bx/timer.h"
#include <bgfx/bgfx.h>
@@ -33,15 +33,105 @@ namespace Game
{
namespace
{
constexpr size_t ChunkSize = 1024;
constexpr size_t MaxFileSize = ChunkSize * 1024 * 1024;
constexpr size_t MaxChunkCount = MaxFileSize / ChunkSize;
bool BufferedFileRead(FILE* const file, uint8_t* const writePtrIn, size_t& outTotalReadCount)
{
uint8_t* writePtr = writePtrIn;
for (int32_t i = 0; i < MaxChunkCount; ++i)
{
size_t readCount = std::fread(writePtr, 1, ChunkSize, file);
writePtr += readCount;
outTotalReadCount += readCount;
if (readCount != ChunkSize)
{
if (std::feof(file))
{
return true;
}
int err = std::ferror(file);
if (err != 0)
{
LOG_ERROR("Error reading file: %i", err);
return false;
}
LOG_ERROR("This should never happen!");
return false;
}
}
if (!std::feof(file))
{
LOG_ERROR("File too big to be read!");
return false;
}
return true;
}
const bgfx::Memory* LoadBinaryFile(const char* path, int32_t retryCount = 1)
{
FILE* file = nullptr;
for (int32_t i = 0; i < retryCount; ++i)
{
file = std::fopen(path, "rb");
if (file == nullptr)
{
if (i < retryCount - 1)
{
std::this_thread::sleep_for(100ms);
LOG_WARN("Failed to open file, retrying...");
break;
}
else
{
LOG_ERROR("Failed to open file!");
return nullptr;
}
}
}
uint8_t* dataPtr = AllocateScratch(MaxFileSize);
if (dataPtr == nullptr)
{
LOG_ERROR("Failed to load file, exceeded scratch memory! %s", path);
return nullptr;
}
uint64_t totalReadCount = 0;
bool success = BufferedFileRead(file, dataPtr, totalReadCount);
std::fclose(file);
if (!success)
{
LOG_ERROR("Failed to read file %s", path);
ResizeLastScratchAlloc(0);
return nullptr;
}
if (!ResizeLastScratchAlloc(totalReadCount))
{
LOG_ERROR("This should never happen!");
return nullptr;
}
return bgfx::makeRef(dataPtr, totalReadCount);
}
const bgfx::Memory* loadFile(const char* path, bool appendZero = false, int32_t retryCount = 1)
{
FILE* file;
for (int32_t i = 0; i < retryCount; ++i)
{
file = fopen(path, "rb");
file = std::fopen(path, "rb");
if (file == nullptr && i < retryCount - 1)
{
std::this_thread::sleep_for(100ms);
LOG_WARN("Failed to open file, retrying...");
break;
}
@@ -49,8 +139,13 @@ namespace Game
long fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
long fileSizeX = appendZero ? fileSize + 1 : fileSize;
long fileSizeX = appendZero ? (fileSize + 1) : fileSize;
void* rawMem = AllocateScratch(fileSizeX);
if (rawMem == nullptr)
{
LOG_ERROR("Failed to load file, exceeded scratch memory! %s", path);
return nullptr;
}
const bgfx::Memory* mem = bgfx::makeRef(rawMem, fileSizeX);
fread(mem->data, 1, fileSize, file);
if (appendZero)
@@ -62,6 +157,7 @@ namespace Game
return mem;
}
LOG_WARN("File inaccessible! %s", path);
return nullptr;
}
@@ -123,8 +219,8 @@ namespace Game
bgfx::TextureHandle handle = BGFX_INVALID_HANDLE;
bx::Error err;
const bgfx::Memory* data = loadFile(_filePath.getCPtr());
if (data == nullptr)
const bgfx::Memory* data = LoadBinaryFile(_filePath.getCPtr());
if (data == nullptr || data->data == nullptr || data->size == 0)
{
LOG_WARN("Failed to find image %s", _filePath.getCPtr());
return handle;
@@ -140,8 +236,18 @@ namespace Game
*_orientation = imageContainer.m_orientation;
}
const bgfx::Memory* mem =
bgfx::makeRef(data->data + imageContainer.m_offset, data->size - imageContainer.m_offset);
// We're working with ktx textures, so the size should be in front of the actual data
const uint32_t* texSizePtr = reinterpret_cast<uint32_t*>(data->data + imageContainer.m_offset);
uint8_t* dataPtr = data->data + imageContainer.m_offset + sizeof(uint32_t);
uint32_t dataSize = data->size - imageContainer.m_offset - sizeof(uint32_t);
// Extra sanity check
if (*texSizePtr != dataSize)
{
LOG_WARN("Texture size sanity check failed! %u != %u", texSizePtr, dataSize);
return handle;
}
const bgfx::Memory* mem = bgfx::makeRef(dataPtr, dataSize);
if (NULL != _info)
{
@@ -198,130 +304,6 @@ namespace Game
return handle;
}
void DitherGen(DitherData& data, int32_t recursion)
{
data.Points[0] = {0.0f, 0.0f};
data.Points[1] = {0.5f, 0.5f};
data.Points[2] = {0.5f, 0.0f};
data.Points[3] = {0.0f, 0.5f};
data.PointCount = 4;
for (int32_t i = 0; i < data.BrightnessBucketCount; ++i)
data.BrightnessBuckets[i] = 0;
for (int32_t recursionLevel = 0; recursionLevel < recursion; ++recursionLevel)
{
int32_t startCount = data.PointCount;
float offset = bx::pow(0.5f, recursionLevel + 1);
for (int32_t i = 0; i < 4; ++i)
{
for (int32_t j = 0; j < startCount; ++j)
{
data.Points[data.PointCount] = data.Points[j] + data.Points[i] * offset;
data.PointCount++;
}
}
}
uint64_t dotsPerSide = bx::round(bx::pow(2, recursion));
data.DitherTexDepth = dotsPerSide * dotsPerSide;
data.DitherTexWH = 16 * dotsPerSide;
uint64_t texPixelCount = data.DitherTexWH * data.DitherTexWH * data.DitherTexDepth;
if (BX_COUNTOF(DitherData::DitherTex) < texPixelCount)
{
LOG_ERROR("Too many pixels: %llu", texPixelCount);
return;
}
float invRes = 1.0f / data.DitherTexWH;
for (int32_t z = 0; z < data.DitherTexDepth; ++z)
{
int32_t dotCount = z + 1;
float dotArea = 0.5f / dotCount;
float dotRadius = bx::sqrt(dotArea / bx::kPi);
int zOffset = z * data.DitherTexWH * data.DitherTexWH;
for (int32_t y = 0; y < data.DitherTexWH; ++y)
{
int32_t yOffset = y * data.DitherTexWH;
for (int32_t x = 0; x < data.DitherTexWH; ++x)
{
Vec2 point{(x + 0.5f) * invRes, (y + 0.5f) * invRes};
float dist = bx::kFloatInfinity;
for (int32_t i = 0; i < dotCount; ++i)
{
Vec2 vec = point - data.Points[i];
Vec2 wrappedVec{bx::mod(vec.x + 0.5f, 1.0f) - 0.5f, bx::mod(vec.y + 0.5f, 1.0f) - 0.5f};
float curDist = wrappedVec.Magnitude();
dist = bx::min(dist, curDist);
}
dist = dist / (dotRadius * 2.4f);
float val = bx::clamp(1.0f - dist, 0.0f, 1.0f);
data.DitherTex[x + yOffset + zOffset] = Vec4{val, val, val, 1.0f};
// data.DitherTex[x + yOffset + zOffset] = Vec4{1.0, 0.0f, 0.0f, 1.0f};
int32_t bucket = bx::clamp(uint32_t(val * DitherData::BrightnessBucketCount),
0,
DitherData::BrightnessBucketCount - 1);
data.BrightnessBuckets[bucket] += 1;
}
}
}
// Brightness ramp
int32_t sum = 0;
for (int32_t i = 0; i < data.BrightnessBucketCount; ++i)
{
sum += data.BrightnessBuckets[data.BrightnessBucketCount - 1 - i];
data.BrightnessRamp[i + 1] = sum / (float)texPixelCount;
}
// Upload textures
if (isValid(data.PreviewTex))
{
bgfx::destroy(data.PreviewTex);
data.PreviewTex = BGFX_INVALID_HANDLE;
}
if (isValid(data.FinalTex))
{
bgfx::destroy(data.FinalTex);
data.FinalTex = BGFX_INVALID_HANDLE;
}
if (isValid(data.RampTex))
{
bgfx::destroy(data.RampTex);
data.RampTex = BGFX_INVALID_HANDLE;
}
const bgfx::Memory* memPreview = bgfx::makeRef(data.DitherTex, texPixelCount * sizeof(Vec4));
const bgfx::Memory* memFinal = bgfx::makeRef(data.DitherTex, texPixelCount * sizeof(Vec4));
const bgfx::Memory* memRamp = bgfx::makeRef(data.BrightnessRamp, sizeof(data.BrightnessRamp));
data.PreviewTex = bgfx::createTexture2D(data.DitherTexWH,
data.DitherTexWH * data.DitherTexDepth,
false,
false,
bgfx::TextureFormat::RGBA32F,
0,
memPreview);
data.FinalTex = bgfx::createTexture3D(data.DitherTexWH,
data.DitherTexWH,
data.DitherTexDepth,
false,
bgfx::TextureFormat::RGBA32F,
0,
memFinal);
data.RampTex = bgfx::createTexture2D(
BX_COUNTOF(data.BrightnessRamp), 1, false, 1, bgfx::TextureFormat::R32F, 0, memRamp);
if (!isValid(data.DitherSampler))
{
data.DitherSampler = bgfx::createUniform("s_ditherSampler", bgfx::UniformType::Sampler);
}
if (!isValid(data.RampSampler))
{
data.RampSampler = bgfx::createUniform("s_rampSampler", bgfx::UniformType::Sampler);
}
}
GameRendering* Instance = nullptr;
} // namespace
@@ -331,18 +313,25 @@ namespace Game
return *Instance;
}
void GameRendering::Setup()
void GameRendering::Setup(const RenderingSetup& setup)
{
LOG("--- RENDERING STARTUP ---");
ZoneScopedN("Setup");
SetupData = setup;
if (Instance != nullptr) LOG_WARN("old rendering wasn't destroyed!");
Instance = this;
SharedData& shared = GetShared();
bgfx::Init init;
init.type = bgfx::RendererType::Direct3D12;
init.type = bgfx::RendererType::Direct3D11;
#ifdef _DEBUG
init.debug = true;
// init.debug = false;
#else
init.debug = false;
#endif
init.platformData.nwh = shared.Window.Handle;
init.platformData.ndt = nullptr;
init.platformData.type = bgfx::NativeWindowHandleType::Default;
@@ -363,21 +352,19 @@ namespace Game
{
LOG("BGFX setup succeded!");
}
// bgfx::setDebug(BGFX_DEBUG_TEXT);
bgfx::setViewClear(MainViewID, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x3399FFff, 1.0f, 0);
bgfx::setViewRect(MainViewID, 0, 0, shared.Window.WindowWidth, shared.Window.WindowHeight);
DefaultSampler = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler);
Textures[0].Handle = loadTexture(
bx::FilePath{"models/body.dds"}, BGFX_TEXTURE_NONE | BGFX_SAMPLER_NONE, 0, &Textures[0].Info, nullptr);
Textures[0].SamplerHandle = DefaultSampler;
LoadTextures();
LoadModels(Models, ModelCount);
Materials[0] =
Material::LoadFromShader("vert", "frag", MainViewID, Textures[0].Handle, Textures[0].SamplerHandle);
ReloadShaders();
if (SetupData.UseImgui)
{
imguiCreate();
SetImguiStyle();
if (!ImGui_ImplSDL3_InitForOther(shared.Window.SDLWindow))
{
LOG_ERROR("Failed to set up imgui implementation!");
@@ -392,6 +379,7 @@ namespace Game
// platIO.Platform_DestroyWindow = TODO;
// platIO.Platform_SetWindowSize = TODO;
// platIO.Platform_RenderWindow = TODO;
}
GameInstance& inst = GetInstance();
if (!inst.IsInitialized)
@@ -399,6 +387,8 @@ namespace Game
inst.Time.StartTime = bx::getHPCounter();
}
if (SetupData.UseImgui)
{
if (inst.DebugData.ImguiIniSize > 0)
{
ImGui::LoadIniSettingsFromMemory(inst.DebugData.ImguiIni, inst.DebugData.ImguiIniSize);
@@ -407,6 +397,7 @@ namespace Game
{
ImGui::LoadIniSettingsFromDisk("imgui.ini");
}
}
DitherGen(DitherTextures, DitherRecursion);
}
@@ -416,9 +407,12 @@ namespace Game
SharedData& shared = GetShared();
for (uint16_t i = 0; i < shared.Window.SDLEventCount; ++i)
{
if (SetupData.UseImgui)
{
ImGui_ImplSDL3_ProcessEvent(&shared.Window.SDLEvents[i]);
}
}
shared.Window.SDLEventCount = 0;
// Resize if necessary
@@ -435,228 +429,98 @@ namespace Game
FileChangeNotification* shaderChange = nullptr;
if (shared.Dev.ChangedShaderCount > 0)
{
char buf[1024]{0};
for (int32_t i = 0; i < shared.Dev.ChangedShaderCount; ++i)
{
wcstombs(buf, shared.Dev.ChangedShaders[i].FileName, sizeof(buf));
LOG("Changed: %s", buf);
}
shared.Dev.ChangedShaderCount = 0;
// TODO: when to destroy shader?
// TODO: only reload changed shaders
bgfx::ShaderHandle vertexShader = loadShader("vert");
bgfx::ShaderHandle fragmentShader = loadShader("frag");
if (isValid(vertexShader) && isValid(fragmentShader))
{
bgfx::ProgramHandle newProgram = bgfx::createProgram(vertexShader, fragmentShader, true);
if (isValid(newProgram))
{
Materials[0].Shader = newProgram;
LastShaderLoadTime = GetInstance().Time.Now;
}
else
{
LOG_WARN("Failed to load shader!");
LastShaderLoadTime = -1.0f;
}
}
ReloadShaders();
}
}
void GameRendering::RenderDebugUI()
void GameRendering::LoadTextures()
{
ZoneScopedN("DebugUI");
auto& shared = GetShared();
auto& debug = GetInstance().DebugData;
auto& level = GetInstance().GameLevel;
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
for (int32_t i = 0; i < MaxTextures; ++i)
{
debug.DebugCardRotation++;
if (debug.DebugCardRotation >= 4) debug.DebugCardRotation = 0;
}
if (ImGui::Begin("Rendering"))
{
if (LastShaderLoadTime >= 0.0f)
{
ImGui::TextColored({0.2f, 0.9f, 0.2f, 1.0f},
"Shader loaded %.0f seconds ago",
GetInstance().Time.Now - LastShaderLoadTime);
}
else
{
ImGui::TextColored({0.9f, 0.2f, 0.2f, 1.0f}, "Shader load Failiure!");
}
if (ImGui::Button("Reload Meshes"))
{
LoadModels(Models, ModelCount);
}
ImGui::SameLine();
if (ImGui::Button("Reload Level"))
{
level = {};
level.Setup(shared.Game);
}
ImGui::Checkbox("Show ImGui Demo", &debug.ShowImguiDemo);
if (debug.ShowImguiDemo) ImGui::ShowDemoWindow(&debug.ShowImguiDemo);
ImGui::Separator();
ImGui::Checkbox("Cubes", &level.Cubes.IsEnabled);
ImGui::Checkbox("Tests", &level.Tests.IsEnabled);
ImGui::Checkbox("PuzzleTiles", &level.PuzzleTiles.IsEnabled);
ImGui::Checkbox("UIQuads", &level.UIQuads.IsEnabled);
if (ImGui::Button("Dithergen"))
{
DitherGen(DitherTextures, DitherRecursion);
}
ImGui::SameLine();
ImGui::SliderInt("Recursion", &DitherRecursion, 1, 4);
ImGui::Text(
"%ux%ux%u", DitherTextures.DitherTexWH, DitherTextures.DitherTexWH, DitherTextures.DitherTexDepth);
if (!isValid(DitherTextures.PreviewTex))
{
ImGui::Text("Invalid Texture");
}
else
{
ImGui::Image(DitherTextures.PreviewTex.idx,
{(float)DitherTextures.DitherTexWH,
(float)DitherTextures.DitherTexWH * DitherTextures.DitherTexDepth});
}
if (isValid(DitherTextures.RampTex))
{
ImGui::Image(DitherTextures.RampTex.idx, {BX_COUNTOF(DitherTextures.BrightnessRamp), 8});
}
Vec3 quadPos = level.UIQuads.Get({0}).EData.Transform.GetPosition();
ImGui::Text("%f %f %f", quadPos.x, quadPos.y, quadPos.z);
if (ImGui::ColorEdit3("Base Color", &DefaultBaseColor.x))
{
auto& tiles = GetInstance().GameLevel.PuzzleTiles;
for (int32_t i = 0; i < tiles.Count; ++i)
{
tiles.Data[i].EData.BaseColor = DefaultBaseColor;
}
}
if (ImGui::ColorEdit3("Dot Color", &DefaultTileColor.x))
{
auto& tiles = GetInstance().GameLevel.PuzzleTiles;
for (int32_t i = 0; i < tiles.Count; ++i)
{
tiles.Data[i].EData.TestColor = DefaultTileColor;
}
Textures[i] = {};
}
ImGui::Text("Shader log:");
ImGui::TextWrapped("%s", GetShared().Dev.ShaderLog);
bx::Error err;
bx::DirectoryReader reader{};
if (!reader.open("textures", &err) || !err.isOk())
{
LOG_ERROR("Failed to read textures dir: %s", err.getMessage());
}
ImGui::End();
if (ImGui::Begin("Puzzles"))
bx::FileInfo info;
int32_t textureFilePathCount = 0;
bx::FilePath textureFilePaths[GameRendering::MaxTextures];
while (err.isOk())
{
if (ImGui::Button("Add"))
int32_t res = reader.read(&info, sizeof(info), &err);
if (res == 0) break; // EOF
if (res != sizeof(info))
{
bool found = false;
for (int32_t i = 0; i < BX_COUNTOF(level.Puzzles); ++i)
{
auto& puz = level.Puzzles[i].Data;
if (puz.ID == UINT16_MAX)
{
bx::strCopy(puz.PuzzleName, sizeof(puz.PuzzleName), "Unnamed Puzzle");
puz.ID = i;
found = true;
LOG_ERROR("Dir iter error: %s", err.getMessage());
break;
}
}
if (!found)
const bx::StringView ext = info.filePath.getExt();
bool isDDS = bx::strCmp(ext, ".dds") == 0;
bool isKTX = bx::strCmp(ext, ".ktx") == 0;
if ((isDDS || isKTX) && !ext.isEmpty() && info.type == bx::FileType::File)
{
LOG_ERROR("Too many puzzles!");
}
}
ImGui::Separator();
for (int32_t i = 0; i < BX_COUNTOF(level.Puzzles); ++i)
if (textureFilePathCount >= GameRendering::MaxTextures)
{
auto& puzzleData = level.Puzzles[i].Data;
if (puzzleData.ID == UINT16_MAX) continue;
LOG_ERROR("Texture limit reached!");
break;
}
textureFilePaths[textureFilePathCount] = info.filePath;
textureFilePathCount++;
}
}
LOG("Found %u textures!", textureFilePathCount);
bool isSelected = debug.SelectedDebugLevel == i;
ImGui::PushID("selectable");
if (ImGui::Selectable(puzzleData.PuzzleName, isSelected))
for (int32_t i = 0; i < textureFilePathCount; ++i)
{
debug.SelectedDebugLevel = isSelected ? UINT16_MAX : i;
}
ImGui::PopID();
bx::FilePath fullPath{"textures"};
fullPath.join(textureFilePaths[i]);
Textures[i].RenderHandle =
loadTexture(fullPath, BGFX_TEXTURE_NONE | BGFX_SAMPLER_NONE, 0, &Textures[i].Info, nullptr);
Textures[i].SamplerHandle = DefaultSampler;
Textures[i].TexHandle.TextureIdx = i;
Textures[i].TexHandle.Asset.Idx = CrcPath(fullPath.getCPtr());
auto& debug = GetInstance().DebugData;
if (debug.AssetCount < debug.MaxAssets)
{
debug.AssetHandles[debug.AssetCount] = Textures[i].TexHandle.Asset;
bx::strCopy(debug.AssetHandlePaths[debug.AssetCount],
sizeof(debug.AssetHandlePaths[debug.AssetCount]),
fullPath.getCPtr());
++debug.AssetCount;
}
}
ImGui::End();
if (debug.SelectedDebugLevel < BX_COUNTOF(level.Puzzles))
{
if (!Puzzle::RenderDebugUI(level.Puzzles[debug.SelectedDebugLevel].Data))
{
debug.SelectedDebugLevel = UINT16_MAX;
}
}
if (ImGui::Begin("Cards"))
{
Generated::StaticPuzzleData& staticData = Puzzle::GetStaticPuzzleData();
if (ImGui::Button("Save"))
{
Puzzle::SaveStaticPuzzleData();
}
ImGui::SameLine();
if (ImGui::Button("Reload"))
{
Puzzle::LoadStaticPuzzleData();
}
for (int32_t i = 0; i < BX_COUNTOF(staticData.Cards); ++i)
void GameRendering::ReloadShaders()
{
ImGui::Separator();
Generated::StaticPuzzleCard& card = staticData.Cards[i];
ImGui::PushID(i);
char cardName[64]{0};
bx::snprintf(cardName, sizeof(cardName), "%i", i);
ImGui::Selectable(cardName);
if (ImGui::BeginDragDropSource())
{
Puzzle::DrawCard(card, debug.DebugCardRotation, ImGui::GetCursorScreenPos());
ImGui::SetDragDropPayload("cardtype", &i, sizeof(i));
ImGui::EndDragDropSource();
}
Tools::ModelDropdown(card.ModelHandle);
for (int8_t y = 0; y < Puzzle::Config::CardSize; ++y)
{
ImGui::PushID(y);
for (int8_t x = 0; x < Puzzle::Config::CardSize; ++x)
{
if (x > 0) ImGui::SameLine();
ImGui::PushID(x);
auto& node = Puzzle::EditCardNodeAt(card, 0, x, y);
if (ImGui::Button(Generated::PuzzleElementType::ShortName[node], {26, 24}))
{
int32_t newVal = int32_t(node) + 1;
if (newVal >= Generated::PuzzleElementType::EntryCount)
{
newVal = 0;
}
node = Generated::PuzzleElementType::Enum(newVal);
}
ImGui::PopID();
}
ImGui::PopID();
}
ImGui::PopID();
}
}
ImGui::End();
Materials[Gen::EMaterial::Default] = Material::LoadFromShader("dither/vert", "dither/frag", MainViewID);
Materials[Gen::EMaterial::UI] = Material::LoadFromShader("normal/vert", "normal/frag", MainViewID);
}
void GameRendering::Update()
{
ZoneScopedN("Rendering");
SharedData& shared = GetShared();
auto& time = GetInstance().Time;
HandleEvents();
// Start Rendering
if (SetupData.UseImgui)
{
ZoneScopedN("Imgui Start Frame");
imguiBeginFrame(20);
@@ -664,21 +528,19 @@ namespace Game
ImGui::DockSpaceOverViewport(0, 0, ImGuiDockNodeFlags_PassthruCentralNode);
}
if (UIVisible == UIVisibilityState::Debug)
{
RenderDebugUI();
}
Tools::RenderDebugUI(*this);
GetInstance().GameLevel.Update();
GetInstance().GameLevel.Render(MainViewID, Models, Materials);
GetInstance().GameLevel.Render(MainViewID, Models, Materials, Textures);
// Finish Frame
if (SetupData.UseImgui)
{
ZoneScopedN("Imgui End Frame");
imguiEndFrame();
}
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
if (SetupData.UseImgui && ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
ZoneScopedN("Imgui Platform Update");
ImGui::UpdatePlatformWindows();
@@ -697,6 +559,25 @@ namespace Game
{
ZoneScopedN("Shutdown");
LOG("--- RENDERING_SHUTDOWN ---");
for (int32_t i = 0; i < BX_COUNTOF(Textures); ++i)
{
if (isValid(Textures[i].RenderHandle))
{
bgfx::destroy(Textures[i].RenderHandle);
Textures[i].RenderHandle = {bgfx::kInvalidHandle};
}
}
for (int32_t i = 0; i < ModelCount; ++i)
{
bgfx::destroy(Models[i].VertexBuffer);
bgfx::destroy(Models[i].IndexBuffer);
}
ModelCount = 0;
CleanupDitherData(DitherTextures);
if (SetupData.UseImgui)
{
ImGui::SaveIniSettingsToDisk("imgui.ini");
auto& debug = GetInstance().DebugData;
const char* iniData = ImGui::SaveIniSettingsToMemory(reinterpret_cast<uint64_t*>(&debug.ImguiIniSize));
@@ -704,12 +585,13 @@ namespace Game
bx::memCopy(debug.ImguiIni, iniData, bx::min(debug.ImguiIniSize, BX_COUNTOF(InstanceDebugData::ImguiIni)));
ImGui_ImplSDL3_Shutdown();
imguiDestroy();
}
bgfx::shutdown();
Instance = nullptr;
}
Material Material::LoadFromShader(
const char* vertPath, const char* fragPath, uint16_t view, bgfx::TextureHandle tex, bgfx::UniformHandle sampler)
Material Material::LoadFromShader(const char* vertPath, const char* fragPath, uint16_t view)
{
BX_ASSERT(vertPath != nullptr && fragPath != nullptr, "Invalid shader path!");
bgfx::ShaderHandle vertexShader = loadShader(vertPath);
@@ -720,26 +602,25 @@ namespace Game
mat.State = 0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS |
BGFX_STATE_CULL_CCW | BGFX_STATE_MSAA;
mat.Uniforms[Material::UTime] = bgfx::createUniform("u_time", bgfx::UniformType::Vec4);
mat.Uniforms[Material::UDotColor] = bgfx::createUniform("u_testColor", bgfx::UniformType::Vec4);
mat.Uniforms[Material::UDotColor] = bgfx::createUniform("u_dotColor", bgfx::UniformType::Vec4);
mat.Uniforms[Material::UTexInfo] = bgfx::createUniform("u_texInfo", bgfx::UniformType::Vec4);
mat.Uniforms[Material::UBaseColor] = bgfx::createUniform("u_baseColor", bgfx::UniformType::Vec4);
mat.Textures[0].Handle = tex;
mat.Textures[0].SamplerHandle = sampler;
mat.ViewID = view;
return mat;
}
uint16_t GameRendering::GetModelHandleFromPath(const char* path)
Gen::ModelHandle GameRendering::GetModelHandleFromPath(const char* path)
{
uint32_t AssetHandle = CrcPath(path);
for (int32_t i = 0; i < ModelCount; ++i)
{
if (Models[i].AssetHandle == AssetHandle)
if (Models[i].Handle.Asset.Idx == AssetHandle)
{
return i;
return Models[i].Handle;
}
}
return 0;
LOG_WARN("Failed to find model for path %s", path);
return {};
}
} // namespace Game

View File

@@ -4,8 +4,8 @@
#include <bx/string.h>
#include <cstdint>
#include "../Global.h"
#include "imgui.h"
#include "../../gen/Generated.h"
#include "Dither.h"
union SDL_Event;
@@ -26,17 +26,42 @@ namespace Game
struct Texture
{
bgfx::UniformHandle SamplerHandle;
bgfx::TextureHandle Handle;
bgfx::UniformHandle SamplerHandle = {bgfx::kInvalidHandle};
bgfx::TextureHandle RenderHandle = {bgfx::kInvalidHandle};
bgfx::TextureInfo Info;
Gen::TextureHandle TexHandle;
};
struct ModelSocket
{
static constexpr uint16_t MaxSocketNameLength = 64;
Gen::Vec3 Pos;
Gen::Mat4 Rot;
char Name[MaxSocketNameLength]{};
};
struct HeightMap
{
static constexpr uint32_t Width = 64;
static constexpr uint32_t Height = 64;
uint8_t Values[Width * Height]{0};
};
struct Model
{
bgfx::VertexBufferHandle VertexBuffer;
bgfx::IndexBufferHandle IndexBuffer;
static constexpr uint16_t MaxSocketCount = 16;
bgfx::VertexBufferHandle VertexBuffer = {bgfx::kInvalidHandle};
bgfx::IndexBufferHandle IndexBuffer = {bgfx::kInvalidHandle};
bgfx::VertexLayout VertLayout;
uint32_t AssetHandle = UINT16_MAX;
Gen::ModelHandle Handle;
uint16_t SocketCount = 0;
ModelSocket Sockets[MaxSocketCount];
HeightMap Height;
bgfx::TextureHandle HeightMapTexture = {bgfx::kInvalidHandle};
Gen::Vec3 MinPos;
Gen::Vec3 MaxPos;
Gen::Vec3 Size;
char Name[128]{0};
};
struct Material
@@ -51,14 +76,9 @@ namespace Game
bgfx::ProgramHandle Shader;
bgfx::UniformHandle Uniforms[8];
Texture Textures[4];
uint64_t State = 0;
uint32_t ViewID = 0;
static Material LoadFromShader(const char* vertPath,
const char* fragPath,
uint16_t view,
bgfx::TextureHandle = BGFX_INVALID_HANDLE,
bgfx::UniformHandle sampler = BGFX_INVALID_HANDLE);
static Material LoadFromShader(const char* vertPath, const char* fragPath, uint16_t view);
};
enum class UIVisibilityState
@@ -68,29 +88,16 @@ namespace Game
Debug,
};
struct DitherData
struct RenderingSetup
{
static constexpr uint32_t BrightnessBucketCount = 256;
Vec2 Points[4096];
uint32_t PointCount = 0;
Vec4 DitherTex[256 * 256 * 64];
uint32_t DitherTexWH = 0;
uint32_t DitherTexDepth = 0;
int32_t BrightnessBuckets[BrightnessBucketCount];
float BrightnessRamp[BrightnessBucketCount + 1];
bgfx::TextureHandle PreviewTex = BGFX_INVALID_HANDLE;
bgfx::TextureHandle FinalTex = BGFX_INVALID_HANDLE;
bgfx::TextureHandle RampTex = BGFX_INVALID_HANDLE;
bgfx::UniformHandle DitherSampler = BGFX_INVALID_HANDLE;
bgfx::UniformHandle RampSampler = BGFX_INVALID_HANDLE;
ImTextureID PreviewID = 0;
bool UseImgui = true;
};
class GameRendering
{
public:
static constexpr uint32_t MaxModels = 64;
static constexpr uint32_t MaxTextures = 64;
static GameRendering& Get();
public:
@@ -98,26 +105,26 @@ namespace Game
DitherData DitherTextures;
public:
RenderingSetup SetupData;
bgfx::UniformHandle DefaultSampler;
Texture Textures[8];
Texture Textures[MaxTextures];
Material Materials[8];
uint32_t ModelCount = 0;
Model Models[MaxModels];
int32_t LastWidth = 0;
int32_t LastHeight = 0;
uint32_t ResetFlags = 0; // BGFX_RESET_VSYNC;
uint32_t ResetFlags = BGFX_RESET_VSYNC;
uint16_t MainViewID = 10;
float LastShaderLoadTime = 0.0f;
int32_t DitherRecursion = 1;
Vec4 DefaultBaseColor;
Vec4 DefaultTileColor;
public:
void Setup();
void Setup(const RenderingSetup& setup);
void Update();
void HandleEvents();
void RenderDebugUI();
void LoadTextures();
void ReloadShaders();
void Shutdown();
uint16_t GetModelHandleFromPath(const char* path);
Gen::ModelHandle GetModelHandleFromPath(const char* path);
};
} // namespace Game

Some files were not shown because too many files have changed in this diff Show More