Compare commits

..

82 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
142 changed files with 7549 additions and 1728 deletions

1
.gitattributes vendored
View File

@@ -7,3 +7,4 @@
*.pzl filter=lfs diff=lfs merge=lfs -text *.pzl filter=lfs diff=lfs merge=lfs -text
*.exe filter=lfs diff=lfs merge=lfs -text *.exe filter=lfs diff=lfs merge=lfs -text
*.glb 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.

View File

@@ -7,6 +7,7 @@ add_compile_options(-g -gcodeview)
add_link_options(-fuse-ld=lld -g -Wl,--pdb=) add_link_options(-fuse-ld=lld -g -Wl,--pdb=)
endif() endif()
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_compile_definitions("$<$<CONFIG:DEBUG>:DEBUG>") add_compile_definitions("$<$<CONFIG:DEBUG>:DEBUG>")
# set the output directory for built objects. # set the output directory for built objects.
@@ -38,10 +39,12 @@ 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_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) 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) set_property(TARGET PuzGame PROPERTY CXX_STANDARD 17)
target_include_directories(PuzGame PUBLIC dependency/imgui) target_include_directories(PuzGame PUBLIC dependency/imgui)
target_include_directories(PuzGame PUBLIC dependency/tracy/public/) target_include_directories(PuzGame PUBLIC dependency/tracy/public)
target_include_directories(PuzGame PUBLIC dependency/imgui-bgfx)
# Profiling # Profiling
option(TRACY_ENABLE "" OFF) option(TRACY_ENABLE "" OFF)

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-release

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

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/*
# !SDL/src bgfx.cmake/*
# bgfx.cmake/*
tinygltf/* 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,7 +9,7 @@
#include <winnt.h> #include <winnt.h>
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include "Windows.h" #include "Windows.h" // IWYU pragma: keep
namespace WriteTemplates namespace WriteTemplates
{ {
@@ -17,7 +17,7 @@ namespace WriteTemplates
R"END(#pragma once R"END(#pragma once
#include <cstdint> #include <cstdint>
namespace Generated namespace Gen
{ {
struct Serializer; struct Serializer;
struct Deserializer; struct Deserializer;
@@ -25,14 +25,14 @@ namespace Generated
constexpr char FileCppStart[] = R"END(#include "Def.h" constexpr char FileCppStart[] = R"END(#include "Def.h"
#include "Generated.h" #include "Generated.h"
namespace Generated namespace Gen
{ {
)END"; )END";
constexpr char StructHeader2[] = constexpr char StructHeader2[] =
R"END( struct %s R"END( struct %s
{ {
static constexpr uint32_t Hash = %u; static constexpr uint16_t TypeIdx = %u;
)END"; )END";
constexpr char StructField4[] = constexpr char StructField4[] =
@@ -46,8 +46,8 @@ namespace Generated
constexpr char EnumHeader4[] = constexpr char EnumHeader4[] =
R"END( struct %s R"END( struct %s
{ {
static constexpr uint16_t TypeIdx = %u;
static constexpr int32_t EntryCount = %u; static constexpr int32_t EntryCount = %u;
static constexpr uint32_t Hash = %u;
enum Enum : %s enum Enum : %s
{ {
)END"; )END";
@@ -55,6 +55,9 @@ namespace Generated
constexpr char EnumField1[] = constexpr char EnumField1[] =
R"END( %s, R"END( %s,
)END"; )END";
constexpr char EnumFieldNumbered2[] =
R"END( %s = %s,
)END";
constexpr char EnumNamesStart2[] = constexpr char EnumNamesStart2[] =
R"END( }; R"END( };
@@ -71,6 +74,25 @@ namespace Generated
}; };
)END"; )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[] = constexpr char FileEnd[] =
R"END(} R"END(}
)END"; )END";
@@ -85,6 +107,8 @@ namespace Generated
{ {
)END"; )END";
constexpr char SaveFuncBodyType3[] = R"END( isOk = Save(%sobj[i].%s, %u, serializer) && isOk; 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"; )END";
constexpr char SaveFuncBodyEnum1[] = R"END( auto val = (%s)obj[i]; constexpr char SaveFuncBodyEnum1[] = R"END( auto val = (%s)obj[i];
isOk = Save(&val, 1, serializer) && isOk; isOk = Save(&val, 1, serializer) && isOk;
@@ -99,18 +123,148 @@ namespace Generated
)END"; )END";
constexpr char LoadFuncBodyStart1[] = R"END( bool Load(%s* obj, uint32_t count, Deserializer& serializer) 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 LoadFuncBodyQuickLoad2[] = R"END( isOk = Load(%sobj[i].%s, %u, serializer) && isOk;
)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; bool isOk = true;
for (uint32_t i = 0; i < count; ++i) 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"; )END";
constexpr char LoadFuncBodyType3[] = R"END( isOk = Load(%sobj[i].%s, %u, serializer) && isOk; 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"; )END";
constexpr char LoadFuncBodyEnum2[] = R"END( %s& val = (%s&)obj[i]; constexpr char LoadFuncBodyTypeUpgradeEnd[] = R"END( assert(false);
isOk = Load(&val, 1, serializer) && isOk; }
)END"; }
constexpr char LoadFuncBodyEnd[] = R"END( } assert(isOk);
return 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"; )END";
} // namespace WriteTemplates } // namespace WriteTemplates
@@ -164,30 +318,34 @@ void CppFileWriter::WriteInternal(WriteBuffer& buf, const char* templateStr, va_
buf.WrittenBytes += bx::vsnprintf(&buf.Data[buf.WrittenBytes], BufferRequestSize, templateStr, args); buf.WrittenBytes += bx::vsnprintf(&buf.Data[buf.WrittenBytes], BufferRequestSize, templateStr, args);
} }
void CppFileWriter::PrintTypeName(char* buf, void CppFileWriter::PrintTypeName(
int32_t bufSize, char* buf, int32_t bufSize, Def::TypeRef type, const Def::DefinitionFile& definitions, PrintFlags flags)
const Def::FieldType& type,
const Def::DefinitionFile& definitions)
{ {
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)
{
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, Generated::KnownType::CName[size_t(type.Native)]); bx::strCopy(buf, bufSize, t.NativeCName);
} }
else 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) else if (type.FieldKind == Def::EFieldType::DefinedEnum)
{ {
bx::strCopy(buf, bufSize, definitions.Enums[type.TypeIdx].Name); bx::strCopy(buf, bufSize, definitions.Enums[type.TypeIdx].Name);
bx::strCat(buf, bufSize, "::Enum"); if ((int32_t)flags & (int32_t)PrintFlags::PrintFullEnumName)
{
bx::strCat(buf, bufSize, "::Enum");
}
} }
} }
@@ -196,15 +354,32 @@ void CppFileWriter::WriteEnums(const Def::DefinitionFile& definitions)
for (int32_t enumIdx = 0; enumIdx < definitions.EnumCount; ++enumIdx) for (int32_t enumIdx = 0; enumIdx < definitions.EnumCount; ++enumIdx)
{ {
const Def::Enum& e = definitions.Enums[enumIdx]; const Def::Enum& e = definitions.Enums[enumIdx];
bool isFlagsEnum = (uint32_t)e.EnumFlags & (uint32_t)Def::EEnumFlags::FlagsEnum;
Write(WriteTemplates::EnumHeader4, if (!IsValid(e.EnumType))
e.Name, {
e.EntryCount, LOG_ERROR(0, "Invalid enum type (enum %i)", enumIdx);
e.Hash, continue;
Generated::KnownType::CName[(int32_t)e.EnumType.Native]); }
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) for (int32_t entryIdx = 0; entryIdx < e.EntryCount; ++entryIdx)
{ {
Write(WriteTemplates::EnumField1, e.EntryNames[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); Write(WriteTemplates::EnumNamesStart2, "EntryNames", Def::MaxNameLength);
@@ -223,6 +398,23 @@ void CppFileWriter::WriteEnums(const Def::DefinitionFile& definitions)
} }
Write(WriteTemplates::EnumNamesEnd); 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) void CppFileWriter::WriteTypes(const Def::DefinitionFile& definitions)
@@ -230,15 +422,16 @@ void CppFileWriter::WriteTypes(const Def::DefinitionFile& definitions)
for (int32_t typeIdx = 0; typeIdx < definitions.TypeCount; ++typeIdx) for (int32_t typeIdx = 0; typeIdx < definitions.TypeCount; ++typeIdx)
{ {
const Def::Type& t = definitions.Types[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) for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{ {
char Type[64]{0}; char Type[64]{0};
PrintTypeName(Type, sizeof(Type), t.FieldTypes[fieldIdx], definitions); PrintTypeName(Type, sizeof(Type), t.FieldTypes[fieldIdx], definitions);
char Array[32]{0}; char Array[32]{0};
uint32_t ArraySize = t.FieldArraySizes[fieldIdx]; uint32_t ArraySize = t.FieldArraySizes[fieldIdx];
if (ArraySize > 0) if (ArraySize > 0 && ArraySize != UINT32_MAX)
{ {
bx::snprintf(Array, sizeof(Array), "[%u]", ArraySize); bx::snprintf(Array, sizeof(Array), "[%u]", ArraySize);
} }
@@ -254,6 +447,11 @@ void CppFileWriter::WriteSaveLoadMethods(const Def::DefinitionFile& definitions)
for (int32_t enumIdx = 0; enumIdx < definitions.EnumCount; ++enumIdx) for (int32_t enumIdx = 0; enumIdx < definitions.EnumCount; ++enumIdx)
{ {
const Def::Enum& e = definitions.Enums[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); bx::snprintf(nameBuf, sizeof(nameBuf), "%s::Enum", e.Name);
PrintTypeName(fieldBuf, sizeof(fieldBuf), e.EnumType, definitions); PrintTypeName(fieldBuf, sizeof(fieldBuf), e.EnumType, definitions);
@@ -266,35 +464,143 @@ void CppFileWriter::WriteSaveLoadMethods(const Def::DefinitionFile& definitions)
WriteCpp(WriteTemplates::LoadFuncBodyStart1, nameBuf); WriteCpp(WriteTemplates::LoadFuncBodyStart1, nameBuf);
WriteCpp(WriteTemplates::LoadFuncBodyEnum2, fieldBuf, fieldBuf); 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]; const Def::Type& t = definitions.Types[typeIdx];
Write(WriteTemplates::SaveFuncHeader1, t.Name); char typeName[Def::MaxNameLength]{0};
Write(WriteTemplates::LoadFuncHeader1, t.Name); PrintTypeName(typeName, sizeof(typeName), {typeIdx, Def::EFieldType::DefinedClass}, definitions);
WriteCpp(WriteTemplates::SaveFuncBodyStart1, t.Name); Write(WriteTemplates::SaveFuncHeader1, typeName);
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx) Write(WriteTemplates::LoadFuncHeader1, typeName);
WriteCpp(WriteTemplates::SaveFuncBodyStart1, typeName);
if ((int32_t)t.TypeFlags & (int32_t)Def::ETypeFlags::IsNative)
{ {
WriteCpp(WriteTemplates::SaveFuncBodyType3, WriteCpp(WriteTemplates::SaveFuncBodyNative);
t.FieldArraySizes[fieldIdx] > 0 ? "" : "&", }
t.FieldNames[fieldIdx], else
bx::max(1, t.FieldArraySizes[fieldIdx])); {
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{
WriteCpp(WriteTemplates::SaveFuncBodyType3,
t.FieldArraySizes[fieldIdx] > 0 ? "" : "&",
t.FieldNames[fieldIdx],
bx::max(1, t.FieldArraySizes[fieldIdx]));
}
} }
WriteCpp(WriteTemplates::SaveFuncBodyEnd); WriteCpp(WriteTemplates::SaveFuncBodyEnd);
WriteCpp(WriteTemplates::LoadFuncBodyStart1, t.Name); WriteCpp(WriteTemplates::LoadFuncBodyStart1, typeName);
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx) if ((int32_t)t.TypeFlags & (int32_t)Def::ETypeFlags::IsNative)
{ {
WriteCpp(WriteTemplates::LoadFuncBodyType3, WriteCpp(WriteTemplates::LoadFuncBodyNative);
t.FieldArraySizes[fieldIdx] > 0 ? "" : "&", }
t.FieldNames[fieldIdx], else
bx::max(1, t.FieldArraySizes[fieldIdx])); {
WriteCpp(WriteTemplates::LoadFuncBodySetupStart2, typeName, typeName);
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{
WriteCpp(WriteTemplates::LoadFuncBodyQuickLoad2,
t.FieldArraySizes[fieldIdx] > 0 ? "" : "&",
t.FieldNames[fieldIdx],
bx::max(1, t.FieldArraySizes[fieldIdx]));
}
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);
} }
WriteCpp(WriteTemplates::LoadFuncBodyEnd);
} }
} }
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) void CppFileWriter::GenerateCpp(const bx::FilePath& outDir, const Def::DefinitionFile& definitions)
{ {
LOG(0, "Generating output files..."); LOG(0, "Generating output files...");
@@ -307,6 +613,7 @@ void CppFileWriter::GenerateCpp(const bx::FilePath& outDir, const Def::Definitio
WriteEnums(definitions); WriteEnums(definitions);
WriteTypes(definitions); WriteTypes(definitions);
WriteSaveLoadMethods(definitions); WriteSaveLoadMethods(definitions);
WriteMetadata(definitions);
Write(WriteTemplates::FileEnd); Write(WriteTemplates::FileEnd);
WriteCpp(WriteTemplates::FileEnd); WriteCpp(WriteTemplates::FileEnd);
@@ -334,36 +641,3 @@ void CppFileWriter::WriteCpp(const char* templateStr, ...)
WriteInternal(CppWrite, templateStr, args); WriteInternal(CppWrite, templateStr, args);
va_end(args); va_end(args);
} }
void CppFileWriter::WriteTypeDataFile(const bx::FilePath& outDir, const Def::DefinitionFile& definitions)
{
constexpr const char _4cc[] = "TDAT";
constexpr uint32_t version = 1;
bx::FilePath typeDataFilePath = outDir;
typeDataFilePath.join("TypeData.bin");
bx::Error err;
bx::FileWriter writer;
if (!writer.open(typeDataFilePath, false, &err))
{
LOG_ERROR(0, "Failed to open type data file to write: %s", err.getMessage().getCPtr());
return;
}
uint32_t size = sizeof(definitions);
writer.write(_4cc, 4, &err);
if (!err.isOk())
{
LOG_ERROR(0, "Failed to write type data: %s", err.getMessage().getCPtr());
writer.close();
return;
}
writer.write(&version, sizeof(version), &err);
writer.write(&size, sizeof(size), &err);
writer.write(&definitions, sizeof(definitions), &err);
if (!err.isOk())
{
LOG_ERROR(0, "Failed to write type data: %s", err.getMessage().getCPtr());
}
writer.close();
return;
}

View File

@@ -10,6 +10,13 @@ struct WriteBuffer
uint64_t WrittenBytes = 0; uint64_t WrittenBytes = 0;
}; };
enum class PrintFlags : uint32_t
{
None = 0,
PrintFullEnumName = 1 << 0,
PrintNativeTypeName = 1 << 1,
};
struct CppFileWriter struct CppFileWriter
{ {
private: private:
@@ -21,13 +28,18 @@ struct CppFileWriter
void WriteInternal(WriteBuffer& buf, const char* templateStr, va_list args); void WriteInternal(WriteBuffer& buf, const char* templateStr, va_list args);
public: 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 WriteEnums(const Def::DefinitionFile& definitions);
void WriteTypes(const Def::DefinitionFile& definitions); void WriteTypes(const Def::DefinitionFile& definitions);
void WriteSaveLoadMethods(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 GenerateCpp(const bx::FilePath& outDir, const Def::DefinitionFile& definitions);
void Write(const char* templateStr, ...); void Write(const char* templateStr, ...);
void WriteCpp(const char* templateStr, ...); void WriteCpp(const char* templateStr, ...);
void WriteTypeDataFile(const bx::FilePath& outDir, const Def::DefinitionFile& definitions);
}; };

View File

@@ -1,41 +0,0 @@
#include "Def.h"
#define INST(T) \
template bool Save<T>(const T* obj, uint32_t count, Serializer& serializer); \
template bool Load<T>(T * obj, uint32_t count, Deserializer & serializer);
namespace Generated
{
template <typename T> bool Save(const T* 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;
}
template <typename T> bool Load(T* 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;
}
INST(uint8_t)
INST(uint16_t)
INST(uint32_t)
INST(uint64_t)
INST(int8_t)
INST(int16_t)
INST(int32_t)
INST(int64_t)
INST(float)
INST(double)
INST(bool)
INST(char)
} // namespace Generated

View File

@@ -1,125 +0,0 @@
#pragma once
#include "../TypeDef.h"
// #include "../game/Log.h"
#include "bx/string.h"
#include <bx/file.h>
#include <cstdint>
#ifndef LOG_ERROR
#define LOG_ERROR(...)
#endif
namespace Generated
{
struct EmbeddedTypeDef
{
uint32_t Size = sizeof(Def::DefinitionFile);
Def::DefinitionFile Data;
};
struct Serializer
{
bx::Error Err;
bx::FilePath Path;
bx::FileWriter Writer;
bool Init(const bx::FilePath& path)
{
Path = path;
Writer.open(path, false, &Err);
return Err.isOk();
}
template <typename T> bool WriteT(const char* _4cc, const T& data)
{
if (!Write(_4cc, 4)) return false;
uint32_t hash = data.Hash;
if (!Write(&hash, sizeof(hash))) return false;
uint32_t size = sizeof(T);
if (!Write(&size, sizeof(size))) return false;
return Save(&data, 1, *this);
}
bool Write(const void* data, uint32_t size)
{
Writer.write(data, size, &Err);
if (!Err.isOk()) LOG_ERROR("Write error: %s", Err.getMessage().getCPtr());
return Err.isOk();
}
void Finish()
{
Writer.close();
}
};
struct Deserializer
{
bx::Error Err;
bx::FilePath Path;
bx::FileReader Reader;
bool Init(const bx::FilePath& path)
{
Path = path;
Reader.open(path, &Err);
return Err.isOk();
}
bool Read(void* data, uint32_t size)
{
Reader.read(data, size, &Err);
if (!Err.isOk()) LOG_ERROR("Read error: %s", Err.getMessage().getCPtr());
return Err.isOk();
}
template <typename T> bool ReadT(const char* _4cc, T& data)
{
char magic[5]{0};
if (!Read(magic, 4)) return false;
bx::StringView given{_4cc, 4};
bx::StringView loaded{magic, 4};
if (bx::strCmp(given, loaded) != 0)
{
LOG_ERROR("Magic mismatch! %s != %s", _4cc, magic);
return false;
}
uint32_t hash = 0;
if (!Read(&hash, sizeof(hash))) return false;
if (data.Hash != hash)
{
LOG_ERROR("Hash mismatch! %u != %u", data.Hash, hash);
return false;
}
uint32_t size = 0;
if (!Read(&size, sizeof(size))) return false;
if (sizeof(T) != size)
{
LOG_ERROR("Size mismatch! %u != %u", sizeof(T), size);
return false;
}
if (!Load(&data, 1, *this))
{
LOG_ERROR("Failed to load: %s", Err.getMessage().getCPtr());
return false;
}
return true;
}
void Finish()
{
Reader.close();
}
};
template <typename T> bool Save(const T* obj, uint32_t count, Serializer& serializer);
template <typename T> bool Load(T* obj, uint32_t count, Deserializer& serializer);
} // namespace Generated

View File

@@ -1,14 +1,14 @@
#include "Def.h" #include "Def.h"
#include "Generated.h" #include "Generated.h"
namespace Generated namespace Gen
{ {
bool Save(const KnownType::Enum* obj, uint32_t count, Serializer& serializer) bool Save(const KnownType::Enum* obj, uint32_t count, Serializer& serializer)
{ {
bool isOk = true; bool isOk = true;
for (uint32_t i = 0; i < count; ++i) for (uint32_t i = 0; i < count; ++i)
{ {
auto val = (int32_t)obj[i]; auto val = (uint8_t)obj[i];
isOk = Save(&val, 1, serializer) && isOk; isOk = Save(&val, 1, serializer) && isOk;
} }
return isOk; return isOk;
@@ -18,17 +18,209 @@ namespace Generated
bool isOk = true; bool isOk = true;
for (uint32_t i = 0; i < count; ++i) for (uint32_t i = 0; i < count; ++i)
{ {
int32_t& val = (int32_t&)obj[i]; uint8_t& val = (uint8_t&)obj[i];
isOk = Load(&val, 1, serializer) && isOk; isOk = Load(&val, 1, serializer) && isOk;
} }
return 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 Save(const Test* obj, uint32_t count, Serializer& serializer)
{ {
bool isOk = true; bool isOk = true;
for (uint32_t i = 0; i < count; ++i) for (uint32_t i = 0; i < count; ++i)
{ {
isOk = Save(&obj[i].Number, 1, serializer) && isOk; isOk = serializer.Write(&obj[i], sizeof(obj[i])) && isOk;
} }
return isOk; return isOk;
} }
@@ -37,7 +229,7 @@ namespace Generated
bool isOk = true; bool isOk = true;
for (uint32_t i = 0; i < count; ++i) for (uint32_t i = 0; i < count; ++i)
{ {
isOk = Load(&obj[i].Number, 1, serializer) && isOk; isOk = serializer.Read(&obj[i], sizeof(obj[i])) && isOk;
} }
return isOk; return isOk;
} }
@@ -46,9 +238,9 @@ namespace Generated
bool isOk = true; bool isOk = true;
for (uint32_t i = 0; i < count; ++i) for (uint32_t i = 0; i < count; ++i)
{ {
isOk = Save(&obj[i].Width, 1, serializer) && isOk; isOk = serializer.Write(&obj[i], sizeof(obj[i])) && isOk;
isOk = Save(&obj[i].Height, 1, serializer) && isOk; isOk = serializer.Write(&obj[i], sizeof(obj[i])) && isOk;
isOk = Save(obj[i].StrTest, 3, serializer) && isOk; isOk = serializer.Write(&obj[i], sizeof(obj[i])) && isOk;
isOk = Save(&obj[i].T, 1, serializer) && isOk; isOk = Save(&obj[i].T, 1, serializer) && isOk;
} }
return isOk; return isOk;
@@ -58,9 +250,9 @@ namespace Generated
bool isOk = true; bool isOk = true;
for (uint32_t i = 0; i < count; ++i) for (uint32_t i = 0; i < count; ++i)
{ {
isOk = Load(&obj[i].Width, 1, serializer) && isOk; isOk = serializer.Read(&obj[i], sizeof(obj[i])) && isOk;
isOk = Load(&obj[i].Height, 1, serializer) && isOk; isOk = serializer.Read(&obj[i], sizeof(obj[i])) && isOk;
isOk = Load(obj[i].StrTest, 3, serializer) && isOk; isOk = serializer.Read(&obj[i], sizeof(obj[i])) && isOk;
isOk = Load(&obj[i].T, 1, serializer) && isOk; isOk = Load(&obj[i].T, 1, serializer) && isOk;
} }
return isOk; return isOk;

View File

@@ -1,15 +1,15 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
namespace Generated namespace Gen
{ {
struct Serializer; struct Serializer;
struct Deserializer; struct Deserializer;
struct KnownType struct KnownType
{ {
static constexpr uint16_t EnumIdx = 0;
static constexpr int32_t EntryCount = 12; static constexpr int32_t EntryCount = 12;
static constexpr uint32_t Hash = 2024002654; enum Enum : uint8_t
enum Enum : int32_t
{ {
i8, i8,
i16, i16,
@@ -57,12 +57,12 @@ namespace Generated
}; };
struct Test struct Test
{ {
static constexpr uint32_t Hash = 273256278; static constexpr uint16_t TypeIdx = 12;
uint32_t Number = {}; uint32_t Number = {};
}; };
struct Texture struct Texture
{ {
static constexpr uint32_t Hash = 992460010; static constexpr uint16_t TypeIdx = 13;
uint32_t Width = {}; uint32_t Width = {};
uint32_t Height = {}; uint32_t Height = {};
char StrTest[3] = {}; char StrTest[3] = {};
@@ -70,8 +70,76 @@ namespace Generated
}; };
bool Save(const KnownType::Enum* obj, uint32_t count, Serializer& serializer); bool Save(const KnownType::Enum* obj, uint32_t count, Serializer& serializer);
bool Load(KnownType::Enum* obj, uint32_t count, Deserializer& 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 Save(const Test* obj, uint32_t count, Serializer& serializer);
bool Load(Test* obj, uint32_t count, Deserializer& serializer); bool Load(Test* obj, uint32_t count, Deserializer& serializer);
bool Save(const Texture* obj, uint32_t count, Serializer& serializer); bool Save(const Texture* obj, uint32_t count, Serializer& serializer);
bool Load(Texture* obj, uint32_t count, Deserializer& 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 "Gen/Generated.h"
#include "Logging.h" #include "Logging.h"
#include "MiniDef.h" #include "MiniDef.h"
#include "TypeDef.h" #include "TypeDef.h"
#include "bx/hash.h" #include "bx/hash.h"
#include "bx/string.h" #include "bx/string.h"
#include <bx/filepath.h> #include <bx/filepath.h>
@@ -114,6 +114,8 @@ Parser::Result Parser::Parse()
{ {
ReadPtr = &Buffer[0]; ReadPtr = &Buffer[0];
Parser::Result Res = Parser::OK; Parser::Result Res = Parser::OK;
CHECK(LoadNativeTypes());
while (Res == Parser::OK) while (Res == Parser::OK)
{ {
Res = SkipWhitespace(); Res = SkipWhitespace();
@@ -136,6 +138,7 @@ Parser::Result Parser::Parse()
Parser::Result Parser::HandleFileStart() Parser::Result Parser::HandleFileStart()
{ {
Result Res = OK; Result Res = OK;
if (CmpAdvance("type", Res, true) && Res == OK) if (CmpAdvance("type", Res, true) && Res == OK)
{ {
return HandleType(); return HandleType();
@@ -152,6 +155,20 @@ Parser::Result Parser::HandleFileStart()
return Error; 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() Parser::Result Parser::HandleType()
{ {
Result Res = OK; Result Res = OK;
@@ -200,6 +217,13 @@ Parser::Result Parser::HandleType()
CHECK(ReadUint(t.FieldArraySizes[t.FieldCount])); CHECK(ReadUint(t.FieldArraySizes[t.FieldCount]));
CHECK(ExpectChar(")")); 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) else if (CmpAdvance("Default", Res, true) && Res == OK)
{ {
CHECK(ExpectChar("(")); CHECK(ExpectChar("("));
@@ -249,12 +273,21 @@ Parser::Result Parser::HandleEnum()
if (CmpAdvance("(", Res) && Res == OK) if (CmpAdvance("(", Res) && Res == OK)
{ {
CHECK(SkipWhitespace()); CHECK(SkipWhitespace());
Def::FieldType field; CHECK(ReadDefinedFieldType(e.EnumType));
CHECK(ReadNativeFieldType(field));
CHECK(SkipWhitespace()); CHECK(SkipWhitespace());
CHECK(ExpectChar(")")); CHECK(ExpectChar(")"));
CHECK(SkipWhitespace()); 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(ExpectChar("{"));
CHECK(SkipWhitespace()); CHECK(SkipWhitespace());
@@ -338,29 +371,13 @@ Parser::Result Parser::ReadTypeToken()
{ {
Result Res = OK; Result Res = OK;
Def::Type& t = Definitions.Types[Definitions.TypeCount]; 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; if (ReadDefinedFieldType(t.FieldTypes[t.FieldCount]) == OK) return OK;
LOG_ERROR(Line, "Unknown type token!"); LOG_ERROR(Line, "Unknown type token!");
ErrorLine(); ErrorLine();
return Error; return Error;
} }
Parser::Result Parser::ReadNativeFieldType(Def::FieldType& FieldT) Parser::Result Parser::ReadDefinedFieldType(Def::TypeRef& 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)
{ {
Result Res = OK; Result Res = OK;
for (uint16_t i = 0; i < Definitions.TypeCount; ++i) 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]); hash.add(t.FieldArraySizes[i]);
Def::EFieldType fieldType = t.FieldTypes[i].FieldKind; Def::EFieldType fieldType = t.FieldTypes[i].FieldKind;
if (fieldType == Def::EFieldType::Native) if (fieldType == Def::EFieldType::DefinedClass)
{
hash.add(t.FieldTypes[i].Native);
}
else if (fieldType == Def::EFieldType::DefinedClass)
{ {
Def::Type& dependType = Definitions.Types[t.FieldTypes[i].TypeIdx]; Def::Type& dependType = Definitions.Types[t.FieldTypes[i].TypeIdx];
if (dependType.Hash == 0) if (dependType.Hash == 0)
@@ -456,7 +469,7 @@ uint32_t Parser::CalculateTypeHash(const Def::Type& t)
} }
else else
{ {
LOG_ERROR(0, "TODO!"); LOG_ERROR(Line, "TODO!");
} }
} }
return hash.end(); return hash.end();
@@ -466,9 +479,14 @@ void Parser::CalculateHashes()
for (int32_t i = 0; i < Definitions.EnumCount; ++i) for (int32_t i = 0; i < Definitions.EnumCount; ++i)
{ {
Def::Enum& e = Definitions.Enums[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; bx::HashMurmur2A hash;
hash.begin(); hash.begin();
hash.add(e.EnumType.Native); hash.add(Definitions.Types[e.EnumType.TypeIdx].Hash); // TODO: add enum entries?
e.Hash = hash.end(); e.Hash = hash.end();
} }
for (int32_t i = 0; i < Definitions.TypeCount; ++i) for (int32_t i = 0; i < Definitions.TypeCount; ++i)
@@ -519,9 +537,5 @@ int main(int argc, const char** argv)
FileParser.CalculateHashes(); FileParser.CalculateHashes();
Writer.GenerateCpp(outPath, FileParser.Definitions); Writer.GenerateCpp(outPath, FileParser.Definitions);
LOG(0, "Writing type data file");
Writer.WriteTypeDataFile(outPath, FileParser.Definitions);
LOG(0, "Finished writing type data file!");
return 0; return 0;
} }

View File

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

View File

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

View File

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

View File

@@ -23,6 +23,10 @@ constexpr const char* DLLPath = "libPuzGame.dll";
constexpr const wchar_t* DLLWatch = L"libPuzGame2.dll"; constexpr const wchar_t* DLLWatch = L"libPuzGame2.dll";
#endif #endif
constexpr uint64_t KB = 1024LLU;
constexpr uint64_t MB = 1024LLU * 1024LLU;
constexpr uint64_t GB = 1024LLU * 1024LLU * 1024LLU;
namespace namespace
{ {
bx::AllocatorI* defaultAllocator = new bx::DefaultAllocator{}; 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) 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; FileChangeNotification notif;
wcscpy(notif.FileName, notifyData->FileName); wcscpy(notif.FileName, notifyData->FileName);
if (changeType == FileChangeType::DLL) if (changeType == FileChangeType::DLL)
@@ -106,23 +114,25 @@ void FileChangeCheck(HANDLE dirHandle, FileChangeType changeType, const wchar_t*
} }
else if (changeType == FileChangeType::Shader) 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); Sleep(1000);
std::ifstream shaderLogFile("shadercompile.log"); std::ifstream shaderLogFile("shadercompile.log");
if (shaderLogFile.is_open()) if (shaderLogFile.is_open())
{ {
shaderLogFile.seekg(0, std::ios::end); 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.seekg(0);
shaderLogFile.read(Shared.Dev.ShaderLog, size); shaderLogFile.read(Shared.Dev.ShaderLog, size);
shaderLogFile.close(); shaderLogFile.close();
Shared.Dev.ShaderLog[size] = 0;
} }
} }
else if (changeType == FileChangeType::CompiledShader) else if (changeType == FileChangeType::CompiledShader)
{ {
printf("compiled shader change!\n");
DevData.FileWatcher.ShaderQueue.push(&notif); DevData.FileWatcher.ShaderQueue.push(&notif);
} }
printf("detected file change of type %u!\n", changeType);
} }
} }
@@ -254,6 +264,15 @@ bool ReloadDLL()
return true; 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() int main()
{ {
char PathBuf[512]{0}; char PathBuf[512]{0};
@@ -273,7 +292,12 @@ int main()
DevData.FileWatcher.ShaderWatcher.ChangeType = FileChangeType::Shader; DevData.FileWatcher.ShaderWatcher.ChangeType = FileChangeType::Shader;
DevData.FileWatcher.CompiledShaderWatcher.ChangeType = FileChangeType::CompiledShader; 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( bx::strCopy(
DevData.FileWatcher.ShaderWatcher.DirPath, sizeof(DevData.FileWatcher.ShaderWatcher.DirPath), "game/shaders"); DevData.FileWatcher.ShaderWatcher.DirPath, sizeof(DevData.FileWatcher.ShaderWatcher.DirPath), "game/shaders");
bx::strCopy(DevData.FileWatcher.CompiledShaderWatcher.DirPath, bx::strCopy(DevData.FileWatcher.CompiledShaderWatcher.DirPath,
@@ -290,15 +314,9 @@ int main()
HANDLE compiledShaderThread = HANDLE compiledShaderThread =
CreateThread(NULL, 0, FileWatcherThread, &DevData.FileWatcher.CompiledShaderWatcher, 0, &fileWatcherThreadId); CreateThread(NULL, 0, FileWatcherThread, &DevData.FileWatcher.CompiledShaderWatcher, 0, &fileWatcherThreadId);
Shared.Game.PermanentStorageSize = 1024 * 1024; InitMemoryArena(Shared.Game.PermanentArena, MB);
Shared.Game.PermanentStorage = VirtualAllocEx( InitMemoryArena(Shared.Game.EntityArena, MB);
GetCurrentProcess(), NULL, Shared.Game.PermanentStorageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); InitMemoryArena(Shared.Game.TransientArena, 2 * GB);
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);
StartupFunc(Shared); StartupFunc(Shared);
bool isRunning = true; 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

View File

@@ -1,6 +1,8 @@
#include "Gen.h" #include "Gen.h"
namespace Generated #include "bx/math.h"
namespace Gen
{ {
bool IsValid(const ModelHandle& h) bool IsValid(const ModelHandle& h)
{ {
@@ -10,6 +12,10 @@ namespace Generated
{ {
return h.Idx != UINT32_MAX; 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) bool operator==(const AssetHandle& lhs, const AssetHandle& rhs)
{ {
return lhs.Idx == rhs.Idx; return lhs.Idx == rhs.Idx;
@@ -45,4 +51,465 @@ namespace Generated
res.Y += rhs.Y; res.Y += rhs.Y;
return res; return res;
} }
} // namespace Generated
// 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

View File

@@ -1,14 +1,98 @@
#pragma once #pragma once
#include "../gen/Generated.h" #include "../gen/Generated.h"
namespace Generated namespace Gen
{ {
bool IsValid(const AssetHandle& h); bool IsValid(const AssetHandle& h);
bool IsValid(const ModelHandle& h); bool IsValid(const ModelHandle& h);
bool IsValid(const TextureHandle& h);
bool operator==(const AssetHandle& lhs, const AssetHandle& rhs); bool operator==(const AssetHandle& lhs, const AssetHandle& rhs);
bool operator==(const ModelHandle& lhs, const ModelHandle& rhs); bool operator==(const ModelHandle& lhs, const ModelHandle& rhs);
PuzPos operator+=(PuzPos lhs, const PuzPos& rhs); PuzPos operator+=(PuzPos lhs, const PuzPos& rhs);
PuzPos operator+(PuzPos lhs, const PuzPos& rhs); PuzPos operator+(PuzPos lhs, const PuzPos& rhs);
} // namespace Generated
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,95 +1,18 @@
#include "../engine/Shared.h" #include "../engine/Shared.h"
#include "Global.h" #include "Global.h"
#include "Instance.h" #include "Instance.h"
#include "bx/bx.h"
#include "bx/math.h"
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
using namespace Gen;
namespace namespace
{ {
SharedData* SharedInstance = nullptr; SharedData* SharedInstance = nullptr;
Game::GameInstance* GameInst = nullptr; Game::GameInstance* GameInst = nullptr;
} // namespace } // 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, temp.M, pos.M);
bx::mtxInverse(MI.M, M.M);
}
void Transform::UpdateMatrixForCam()
{
Mat4 pos;
bx::mtxTranslate(pos.M, Position.x, Position.y, Position.z);
bx::mtxMul(M.M, pos.M, Rotation.M);
bx::mtxInverse(MI.M, M.M);
}
namespace Game namespace Game
{ {
SharedData& GetShared() SharedData& GetShared()
@@ -114,69 +37,36 @@ namespace Game
GameInst = &instance; GameInst = &instance;
} }
void* AllocateScratch(size_t byteCount, size_t align) uint8_t* AllocateScratch(uint64_t byteCount, uint32_t align)
{ {
size_t offset = GetInstance().UsedScratchAmount; assert(align <= 64); // The alignment of the arena limits the alignment that can be specified here!
uint8_t* base = static_cast<uint8_t*>(GetShared().Game.TransientStorage); auto& arena = GetShared().Game.TransientArena;
uint8_t* current = base + offset; uint64_t offsetAligned = ((arena.Used + align - 1) / align) * align;
size_t offsetAligned = ((offset + align - 1) / align) * align; uint8_t* ptrAligned = arena.Base + offsetAligned;
uint8_t* ptrAligned = base + offsetAligned; uint64_t newOffset = offsetAligned + byteCount;
size_t newOffset = offsetAligned + byteCount; if (newOffset > arena.MaxSize) return nullptr;
if (newOffset > GetShared().Game.TransientStorageSize) return nullptr; arena.Used = newOffset;
GetInstance().UsedScratchAmount = newOffset; arena.LastAllocSize = byteCount;
return reinterpret_cast<void*>(ptrAligned); 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 } // 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 #pragma once
#include "bx/math.h" #include <cstdint>
#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();
};
inline int32_t SetFlags(int32_t in, int32_t flags) inline int32_t SetFlags(int32_t in, int32_t flags)
{ {
@@ -203,33 +23,6 @@ inline bool GetFlag(int32_t in, int32_t flags)
return (in & flags) > 0; 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();
void UpdateMatrixForCam();
};
struct SharedData; struct SharedData;
namespace Game namespace Game
@@ -240,5 +33,7 @@ namespace Game
void SetShared(SharedData& instance); void SetShared(SharedData& instance);
GameInstance& GetInstance(); GameInstance& GetInstance();
void SetInstance(GameInstance& instance); 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 } // namespace Game

View File

@@ -2,17 +2,22 @@
#include "Global.h" #include "Global.h"
#include "Input.h" #include "Input.h"
#include "imgui.h" #include "imgui.h"
#include "imgui_internal.h"
namespace Game namespace Game
{ {
using namespace Gen;
bool IsKeyboardAllowed() bool IsKeyboardAllowed()
{ {
if (GImGui == nullptr) return true;
auto& IO = ImGui::GetIO(); auto& IO = ImGui::GetIO();
return !IO.WantCaptureKeyboard || GetFlag(IO.ConfigFlags, ImGuiConfigFlags_NoKeyboard); return !IO.WantCaptureKeyboard || GetFlag(IO.ConfigFlags, ImGuiConfigFlags_NoKeyboard);
} }
bool IsMouseAllowed() bool IsMouseAllowed()
{ {
if (GImGui == nullptr) return true;
auto& IO = ImGui::GetIO(); auto& IO = ImGui::GetIO();
return !IO.WantCaptureMouse || GetFlag(IO.ConfigFlags, ImGuiConfigFlags_NoMouse); return !IO.WantCaptureMouse || GetFlag(IO.ConfigFlags, ImGuiConfigFlags_NoMouse);
} }
@@ -56,4 +61,11 @@ namespace Game
if (!IsMouseAllowed()) return {}; if (!IsMouseAllowed()) return {};
return {GetShared().Window.MouseDeltaX, GetShared().Window.MouseDeltaY}; 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 } // namespace Game

View File

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

View File

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

View File

@@ -6,6 +6,9 @@
#include "Level.h" #include "Level.h"
#include "Log.h" #include "Log.h"
#include "Puzzle.h" #include "Puzzle.h"
#include "UI.h"
#include "bx/bx.h"
#include "bx/debug.h"
#include "rendering/Rendering.h" #include "rendering/Rendering.h"
#include "SDL3/SDL_mouse.h" #include "SDL3/SDL_mouse.h"
@@ -20,19 +23,33 @@
#include <cstdint> #include <cstdint>
#include <tracy/Tracy.hpp> #include <tracy/Tracy.hpp>
using namespace Gen;
namespace Game namespace Game
{ {
void EntityRenderData::Render(const Model* models, const Material* materials) void EntityRenderData::Render(const Model* models, const Material* materials, const Texture* textures)
{ {
if (!Generated::IsValid(ModelH) || 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; if (!Visible) return;
auto& rendering = GameRendering::Get(); auto& rendering = GameRendering::Get();
Transform.UpdateMatrix(); UpdateMatrix(Transform);
bgfx::setTransform(Transform.M.M); bgfx::setTransform(Transform.M.M);
const Model& currentModel = models[ModelH.ModelIdx]; const Model& currentModel = models[ModelH.ModelIdx];
const Material& currentMaterial = materials[MaterialHandle]; 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::setVertexBuffer(0, currentModel.VertexBuffer);
bgfx::setIndexBuffer(currentModel.IndexBuffer); bgfx::setIndexBuffer(currentModel.IndexBuffer);
bgfx::setState(currentMaterial.State); bgfx::setState(currentMaterial.State);
@@ -41,49 +58,78 @@ namespace Game
timeValues[0] = GetInstance().Time.Now; timeValues[0] = GetInstance().Time.Now;
float texInfo[4]{0.0f}; float texInfo[4]{0.0f};
texInfo[0] = currentMaterial.Textures[0].Info.width; texInfo[0] = textures[0].Info.width;
texInfo[1] = currentMaterial.Textures[0].Info.height; texInfo[1] = textures[0].Info.height;
texInfo[2] = rendering.DitherTextures.DitherTexWH; texInfo[2] = rendering.DitherTextures.DitherTexWH;
texInfo[3] = rendering.DitherTextures.DitherTexDepth; 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(1, rendering.DitherTextures.DitherSampler, rendering.DitherTextures.FinalTex);
bgfx::setTexture(2, rendering.DitherTextures.RampSampler, rendering.DitherTextures.RampTex); bgfx::setTexture(2, rendering.DitherTextures.RampSampler, rendering.DitherTextures.RampTex);
bgfx::setUniform(currentMaterial.Uniforms[Material::UTime], timeValues); 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::UTexInfo], texInfo);
bgfx::setUniform(currentMaterial.Uniforms[Material::UBaseColor], &BaseColor.x); bgfx::setUniform(currentMaterial.Uniforms[Material::UBaseColor], &BaseColor.x);
bgfx::submit(currentMaterial.ViewID, currentMaterial.Shader); 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 namespace
{ {
void UpdatePlayerInputMode() void UpdatePlayerInputMode()
{ {
bool IsGaming = GetInstance().Player.InputM == InputMode::Game; bool IsGaming = GetInstance().Player.InputM == InputMode::Game;
SDL_SetWindowRelativeMouseMode(GetShared().Window.SDLWindow, IsGaming); bool captureMouse = IsGaming && GetInstance().Player.InteractionM == InteractionMode::Walk;
auto& IO = ImGui::GetIO(); SDL_SetWindowRelativeMouseMode(GetShared().Window.SDLWindow, captureMouse);
IO.ConfigFlags = FlagBool(IO.ConfigFlags, ImGuiConfigFlags_NoMouse | ImGuiConfigFlags_NoKeyboard, IsGaming);
GameRendering::Get().UIVisible = IsGaming ? UIVisibilityState::Game : UIVisibilityState::Debug; auto& rendering = GameRendering::Get();
if (rendering.SetupData.UseImgui)
{
auto& IO = ImGui::GetIO();
IO.ConfigFlags =
FlagBool(IO.ConfigFlags, ImGuiConfigFlags_NoMouse | ImGuiConfigFlags_NoKeyboard, captureMouse);
}
rendering.UIVisible = IsGaming ? UIVisibilityState::Game : UIVisibilityState::Debug;
} }
} // namespace } // namespace
void Level::Setup(GameData& data) void Level::Setup(GameData& data)
{ {
LOG("Level setup"); LOG("Level setup");
void* storagePtr = data.EntityStorage; uint8_t* storagePtr = data.EntityArena.Base;
bool needReset = false; bool needReset = false;
needReset |= Cubes.Setup(storagePtr, needReset); needReset |= Cubes.Setup(storagePtr, needReset);
needReset |= Tests.Setup(storagePtr, needReset); needReset |= Tests.Setup(storagePtr, needReset);
needReset |= PuzzleTiles.Setup(storagePtr, needReset); needReset |= PuzzleTiles.Setup(storagePtr, needReset);
needReset |= PuzzleTileCovers.Setup(storagePtr, needReset);
needReset |= UIQuads.Setup(storagePtr, needReset); needReset |= UIQuads.Setup(storagePtr, needReset);
needReset |= LevelEntities.Setup(storagePtr, needReset);
UIQuads.IsEnabled = false;
Tests.IsEnabled = false;
Cubes.IsEnabled = false;
Puzzle::Setup(); Puzzle::Setup();
UIQuads.Count = 0;
PuzzleTiles.Count = 0;
bx::Error err; bx::Error err;
bx::DirectoryReader dirIter; bx::DirectoryReader dirIter;
@@ -105,8 +151,8 @@ namespace Game
fullPath.join(info.filePath); fullPath.join(info.filePath);
LOG("Loading %s", fullPath.getCPtr()); LOG("Loading %s", fullPath.getCPtr());
Generated::Deserializer ser; Gen::Deserializer ser;
Generated::PuzzleData dataBuf; Gen::PuzzleData dataBuf;
if (ser.Init(fullPath, "PZZL") && ser.ReadT(dataBuf)) if (ser.Init(fullPath, "PZZL") && ser.ReadT(dataBuf))
{ {
if (dataBuf.ID >= BX_COUNTOF(Puzzles)) if (dataBuf.ID >= BX_COUNTOF(Puzzles))
@@ -136,26 +182,22 @@ namespace Game
LOG_ERROR("Failed to open puzzle dir at %s:\n%s", puzzleDirPath.getCPtr(), err.getMessage().getCPtr()); 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) PlayerOutsideViewCube = Cubes.New();
{ Cubes.Get(PlayerOutsideViewCube).Setup();
for (uint32_t xx = 0; xx < 11; ++xx)
{
Cube& c = Cubes.Get(Cubes.New());
c.TestX = xx;
c.TestY = yy;
c.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();
} }
UIQuads.Count = 0; UIQuads.Count = 0;
PuzzleTiles.Count = 0; PuzzleTiles.Count = 0;
PuzzleTileCovers.Count = 0;
for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i) for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i)
{ {
if (Puzzles[i].Data.ID != UINT16_MAX) if (Puzzles[i].Data.ID != UINT16_MAX)
@@ -163,8 +205,90 @@ namespace Game
Puzzles[i].Setup(); Puzzles[i].Setup();
} }
} }
LOG("Tiles: %u", PuzzleTiles.Count);
PuzzleUI.Setup();
ReloadLevelEntities();
UpdatePlayerInputMode(); 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() void Level::Update()
@@ -175,8 +299,8 @@ namespace Game
// Input // Input
float delta = GetInstance().Time.Delta; float delta = GetInstance().Time.Delta;
constexpr float moveSpeed = 10.0f; float moveSpeed = player.MovementSpeed;
constexpr float rotSpeed = 1.6f; float rotSpeed = player.MouseSensitivity;
float forwardInput = (GetKey(ScanCode::W) ? 1.0f : 0.0f) + (GetKey(ScanCode::S) ? -1.0f : 0.0f); 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); float rightInput = (GetKey(ScanCode::D) ? 1.0f : 0.0f) + (GetKey(ScanCode::A) ? -1.0f : 0.0f);
@@ -184,7 +308,7 @@ namespace Game
moveInput = bx::normalize(moveInput); moveInput = bx::normalize(moveInput);
bx::Vec3 inputVec = {moveInput.x * delta * moveSpeed, 0.0f, moveInput.y * delta * moveSpeed}; bx::Vec3 inputVec = {moveInput.x * delta * moveSpeed, 0.0f, moveInput.y * delta * moveSpeed};
Vec2 mouseMovement = GetMouseMovement(); 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)) if (GetKeyPressedNow(ScanCode::F1))
{ {
@@ -210,23 +334,57 @@ namespace Game
{ {
player.FreeflyXRot += rotInput.x; player.FreeflyXRot += rotInput.x;
player.FreeflyYRot += rotInput.y; player.FreeflyYRot += rotInput.y;
bx::mtxRotateY(player.FreeflyCamTransform.Rotation.M, player.FreeflyYRot); bx::mtxRotateXYZ(player.FreeflyCamTransform.Rotation.M, player.FreeflyXRot, player.FreeflyYRot, 0.0f);
player.FreeflyCamTransform.RotateLocal({player.FreeflyXRot, 0.0f, 0.0f});
} }
player.FreeflyCamTransform.TranslateLocal({0.0f, 0.0f, -inputVec.z}); TranslateLocal(player.FreeflyCamTransform, {0.0f, 0.0f, inputVec.z});
player.FreeflyCamTransform.TranslateLocal({-inputVec.x, 0.0f, 0.0f}); TranslateLocal(player.FreeflyCamTransform, {inputVec.x, 0.0f, 0.0f});
} }
else if (player.CameraM == CameraMode::Walk) else if (player.CameraM == CameraMode::Walk)
{ {
player.PlayerCamTransform.TranslateLocal({0.0f, 0.0f, -inputVec.z}); auto newTransform = player.PlayerCamTransform;
player.PlayerCamTransform.TranslateLocal({-inputVec.x, 0.0f, 0.0f}); // Global and local are inverted because camera
player.PlayerCamTransform.Position.y = -3.0f; 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.WalkXRot += rotInput.x;
player.WalkYRot += rotInput.y; player.WalkYRot += rotInput.y;
bx::mtxRotateY(player.PlayerCamTransform.Rotation.M, player.WalkYRot); bx::mtxRotateXYZ(player.PlayerCamTransform.Rotation.M, player.WalkXRot, player.WalkYRot, 0.0f);
player.PlayerCamTransform.RotateLocal({player.WalkXRot, 0.0f, 0.0f}); }
if (GetKeyPressedNow(ScanCode::SPACE))
{
player.InteractionM = player.InteractionM == InteractionMode::ReadTablet ? InteractionMode::Walk
: InteractionMode::ReadTablet;
UpdatePlayerInputMode();
} }
// Cubes // Cubes
@@ -236,126 +394,235 @@ namespace Game
} }
// Puzzle tiles // Puzzle tiles
Puzzle::PuzzleSolver solver;
uint16_t activeIdx = GetInstance().DebugData.SelectedDebugLevel;
for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i) for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i)
{ {
if (IsInPuzzle(Puzzles[i], player.PlayerCamTransform.Position))
{
activeIdx = i;
}
Puzzles[i].IsActive = activeIdx == i;
Puzzles[i].Update(); 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); 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"); ZoneScopedN("Level Render");
auto& shared = GetShared(); auto& shared = GetShared();
auto& player = GetInstance().Player;
float proj[16]; bx::mtxProj(&player.Projection.M[0],
bx::mtxProj(proj,
75.0f, 75.0f,
float(shared.Window.WindowWidth) / float(shared.Window.WindowHeight), float(shared.Window.WindowWidth) / float(shared.Window.WindowHeight),
0.1f, 0.1f,
1000.0f, 1000.0f,
bgfx::getCaps()->homogeneousDepth); bgfx::getCaps()->homogeneousDepth);
bx::mtxInverse(&player.ProjectionInverse.M[0], &player.Projection.M[0]);
auto& player = GetInstance().Player; bool isFreefly = player.CameraM == CameraMode::Freefly;
if (player.CameraM == CameraMode::Freefly) Cubes.Get(PlayerOutsideViewCube).EData.Visible = isFreefly;
if (isFreefly)
{ {
player.FreeflyCamTransform.UpdateMatrixForCam(); UpdateMatrix(player.FreeflyCamTransform);
bgfx::setViewTransform(viewId, player.FreeflyCamTransform.M.M, proj); bgfx::setViewTransform(viewId, player.FreeflyCamTransform.MI.M, &player.Projection.M[0]);
} }
else else
{ {
player.PlayerCamTransform.UpdateMatrixForCam(); UpdateMatrix(player.PlayerCamTransform);
bgfx::setViewTransform(viewId, player.PlayerCamTransform.M.M, proj); bgfx::setViewTransform(viewId, player.PlayerCamTransform.MI.M, &player.Projection.M[0]);
} }
Cubes.Render(models, materials); bgfx::touch(viewId);
Tests.Render(models, materials);
PuzzleTiles.Render(models, materials); Cubes.Render(models, materials, textures);
UIQuads.Render(models, materials); 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() void Cube::Setup()
{ {
EData.MaterialHandle = 0; EData.MaterialHandle = EMaterial::UI;
EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf"); EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf");
} }
void Cube::Update() void Cube::Update()
{ {
if (TestX >= 0 && TestY >= 0) EData.Transform.Position = GetInstance().Player.PlayerCamTransform.Position;
{ EData.Transform.Rotation = GetInstance().Player.PlayerCamTransform.Rotation;
double globalTime = GetInstance().Time.Now; EData.Transform.Scale = {0.2f, 0.2f, 0.2f};
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};
}
} }
void TestEntity::Setup() void TestEntity::Setup()
{ {
EData.MaterialHandle = 0; EData.MaterialHandle = EMaterial::Default;
EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/zurg.gltf"); EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/cube.gltf");
EData.Transform.Position = {0.0f, 0.0f, 0.0f};
} }
void WorldPuzzle::Setup() void WorldPuzzle::Setup()
{ {
auto& level = GetInstance().GameLevel; Level& level = GetInstance().GameLevel;
for (int32_t i = 0; i < Puzzle::Config::MaxCardsInPuzzle; ++i) for (int32_t i = 0; i < Puzzle::Config::MaxCardsInPuzzle; ++i)
{ {
TileHandles[i] = level.PuzzleTiles.New(); TileHandles[i] = level.PuzzleTiles.New();
auto& tile = level.PuzzleTiles.Get(TileHandles[i]); PuzzleTileEntity& tile = level.PuzzleTiles.Get(TileHandles[i]);
tile.EData.MaterialHandle = 0; tile.EData.MaterialHandle = EMaterial::Default;
UIPlacedCards[i] = level.UIQuads.New();
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; IsSetup = true;
LOG("finished setup!"); 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() void WorldPuzzle::Update()
{ {
Transform& camTransform = GetInstance().Player.PlayerCamTransform;
Vec3 cameraPos = camTransform.GetPosition() * -1;
Level& level = GetInstance().GameLevel; Level& level = GetInstance().GameLevel;
auto& staticCards = Puzzle::GetStaticPuzzleData().Cards; auto& staticCards = Puzzle::GetStaticPuzzleData().Cards;
auto& visuals = Puzzle::GetStaticPuzzleData().Visuals;
// Board
for (int8_t y = 0; y < Data.HeightTiles / Puzzle::Config::CardSize; ++y) for (int8_t y = 0; y < Data.HeightTiles / Puzzle::Config::CardSize; ++y)
{ {
for (int8_t x = 0; x < Data.WidthTiles / Puzzle::Config::CardSize; ++x) for (int8_t x = 0; x < Data.WidthTiles / Puzzle::Config::CardSize; ++x)
{ {
Generated::PlacedPuzzleCard& card = Data.PlacedCards[y * Puzzle::Config::MaxPuzzleSizeCards + x]; int32_t cardIdx = y * Puzzle::Config::MaxPuzzleSizeCards + x;
auto& tile = level.PuzzleTiles.Get(TileHandles[y * Puzzle::Config::MaxPuzzleSizeCards + x]); Gen::PlacedPuzzleCard& card = Data.PlacedCards[cardIdx];
auto& quad = level.UIQuads.Get(UIPlacedCards[y * Puzzle::Config::MaxPuzzleSizeCards + x]); auto& tile = level.PuzzleTiles.Get(TileHandles[cardIdx]);
bool IsValid = Puzzle::IsValid(card.RefCard); bool isValid = Puzzle::IsValid(card.RefCard);
auto& staticCard = isValid ? staticCards[card.RefCard.Idx] : staticCards[0];
// World Tile
tile.EData.Visible = true; tile.EData.Visible = true;
quad.EData.Visible = true; tile.EData.ModelH = staticCard.BaseModelHandle;
tile.EData.TextureHandle = staticCard.ModelTextureHandle;
tile.EData.ModelH = IsValid ? staticCards[card.RefCard.Idx].ModelHandle : staticCards[0].ModelHandle; tile.EData.DotColor = visuals.TileDotColor;
Vec3 cardPos = { tile.EData.BaseColor = visuals.TileBaseColor;
(float)card.Position.X * Puzzle::Config::CardScaleWorld,
-5.0f, Vec3 cardPos = PuzPosToWorldPos(Data, card.Position.X, card.Position.Y);
(float)card.Position.Y * Puzzle::Config::CardScaleWorld, if (!isValid)
};
if (!IsValid)
{ {
cardPos = {x * Puzzle::Config::CardScaleWorld, -5.0f, y * Puzzle::Config::CardScaleWorld}; cardPos = PuzPosToWorldPos(Data, x, y);
} }
tile.EData.Transform.SetPosition(cardPos); tile.EData.Transform.Position = cardPos + WorldPosition;
bx::mtxRotateY(tile.EData.Transform.Rotation.M, card.Rotation * bx::kPi * 0.5f); bx::mtxRotateY(tile.EData.Transform.Rotation.M, card.Rotation * bx::kPi * -0.5f);
Vec3 fw = {0, -1, 0}; // Covers
quad.EData.Transform.SetPosition(fw); if (IsValid(staticCard.BaseModelHandle))
quad.EData.Transform.Rotation = {}; {
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 } // namespace Game

View File

@@ -1,141 +1,29 @@
#pragma once #pragma once
#include "../engine/Shared.h" #include "../engine/Shared.h"
#include "Global.h" #include "Entity.h"
#include "Log.h"
#include "Puzzle.h" #include "Puzzle.h"
#include "UI.h"
#include "rendering/Rendering.h" #include "rendering/Rendering.h"
#include <bgfx/bgfx.h> #include <bgfx/bgfx.h>
#include <cstdint> #include <cstdint>
#define ENTITY_HANDLE(X) \
struct X \
{ \
uint16_t Idx = UINT16_MAX; \
};
namespace Game 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;
Generated::ModelHandle ModelH;
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 struct WorldPuzzle
{ {
static constexpr Vec2 WorldCardSize{10.0f, 10.0f}; static constexpr Gen::Vec2 WorldCardSize{10.0f, 10.0f};
Generated::PuzzleData Data; Gen::PuzzleData Data;
Vec3 WorldPosition; Gen::Vec3 WorldPosition;
PuzzleTileEntityHandle TileHandles[Puzzle::Config::MaxCardsInPuzzle]; 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 IsSetup = false;
bool IsActive = false;
bool IsSolved = false;
void Setup(); void Setup();
void Update(); void Update();
@@ -146,16 +34,23 @@ namespace Game
public: public:
EntityManager<Cube, CubeHandle, 1024> Cubes; EntityManager<Cube, CubeHandle, 1024> Cubes;
EntityManager<TestEntity, TestEntityHandle, 32> Tests; EntityManager<TestEntity, TestEntityHandle, 32> Tests;
EntityManager<PuzzleTileEntity, PuzzleTileEntityHandle, 1024> PuzzleTiles; EntityManager<PuzzleTileEntity, PuzzleTileEntityHandle, Puzzle::Config::MaxTilesTotal> PuzzleTiles;
EntityManager<UIQuadEntity, UIQuadEntityHandle, 1024> UIQuads; EntityManager<PuzzleTileCover, PuzzleTileCoverHandle, Puzzle::Config::MaxCoversTotal> PuzzleTileCovers;
UIQuadEntityManager UIQuads;
EntityManager<LevelEntity, LevelEntityHandle, 64> LevelEntities;
CubeHandle PlayerOutsideViewCube;
LevelEntityHandle BackgroundEntityHandles[16];
public: public:
Generated::StaticPuzzleData PuzzleData; Gen::StaticPuzzleData PuzzleData;
WorldPuzzle Puzzles[1]; WorldPuzzle Puzzles[Puzzle::Config::MaxVisiblePuzzles];
WorldPuzzleUI PuzzleUI;
public: public:
void Setup(GameData& data); void Setup(GameData& data);
void Update(); 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 } // namespace Game

View File

@@ -1,10 +1,13 @@
#include "Log.h" #include "Log.h"
#include "bx/handlealloc.h" #include "bx/handlealloc.h"
#include "bx/hash.h" #include "bx/hash.h"
#include "bx/timer.h"
#include <bx/string.h> #include <bx/string.h>
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include "Windows.h" #include "Windows.h"
#include "Instance.h"
#define ESC "\x1B[" #define ESC "\x1B["
#define YELLOW ESC "33m" #define YELLOW ESC "33m"
#define RED ESC "31m" #define RED ESC "31m"
@@ -12,9 +15,12 @@
namespace namespace
{ {
char LineBuffer[1024]{0}; char LineBuffer[LogInternal::MaxLineSize]{0};
char OutBuffer[1024]{0}; char OutBuffer[LogInternal::MaxLineSize]{0};
char OutBufferUI[LogInternal::MaxLineSize]{0};
bx::HandleHashMapT<1024> OnceMap; bx::HandleHashMapT<1024> OnceMap;
LogHistory History;
constexpr char LogFormat[]{"%s\n"}; constexpr char LogFormat[]{"%s\n"};
constexpr char WarnFormat[]{YELLOW "%s" END "\n"}; constexpr char WarnFormat[]{YELLOW "%s" END "\n"};
constexpr char ErrorFormat[]{RED "%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::snprintf(LineBuffer, sizeof(LineBuffer), LineFormat, format);
bx::vprintf(LineBuffer, args); bx::vprintf(LineBuffer, args);
bx::vsnprintf(OutBuffer, sizeof(OutBuffer), LineBuffer, args); bx::vsnprintf(OutBuffer, sizeof(OutBuffer), LineBuffer, args);
bx::vsnprintf(OutBufferUI, sizeof(OutBufferUI), format, args);
va_end(args); va_end(args);
OutputDebugStringA(OutBuffer); 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) 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, {}); OnceMap.insert(hash, {});
return false; return false;
} }
LogHistory& GetLogHistory()
{
return History;
}

View File

@@ -27,5 +27,22 @@ enum class ELogType
Error, 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, ...); 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); 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 "Global.h"
#include "Log.h" #include "Log.h"
#include "Mesh.h" #include "Mesh.h"
#include "bgfx/bgfx.h"
#include "bx/bx.h" #include "bx/bx.h"
#include "bx/error.h" #include "bx/error.h"
#include "bx/file.h" #include "bx/file.h"
#include "bx/filepath.h" #include "bx/filepath.h"
#include "bx/hash.h" #include "bx/hash.h"
#include "bx/string.h" #include "bx/string.h"
#include "bx/timer.h"
#include "rendering/Rendering.h" #include "rendering/Rendering.h"
#include "Instance.h" #include "Instance.h"
#include <cstdint>
#define TINYGLTF_IMPLEMENTATION #define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
@@ -20,6 +24,7 @@ namespace Game
{ {
bool LoadMesh(Model& mesh, const char* path, bool isBinary) bool LoadMesh(Model& mesh, const char* path, bool isBinary)
{ {
bx::strCopy(mesh.Name, sizeof(mesh.Name), path);
mesh.VertLayout.begin() mesh.VertLayout.begin()
.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float) .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
.add(bgfx::Attrib::Normal, 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::Primitive primitive = model.meshes[0].primitives[0];
{ {
tinygltf::Accessor accessor = model.accessors.at(primitive.indices); tinygltf::Accessor indexAccessor = model.accessors.at(primitive.indices);
tinygltf::BufferView bufferView = model.bufferViews.at(accessor.bufferView); tinygltf::BufferView indexBufferView = model.bufferViews.at(indexAccessor.bufferView);
tinygltf::Buffer buffer = model.buffers[bufferView.buffer]; int32_t indexStride = sizeof(uint16_t);
const bgfx::Memory* ibMem = bgfx::alloc(bufferView.byteLength); tinygltf::Buffer indexBuffer = model.buffers[indexBufferView.buffer];
bx::memCopy(ibMem->data, &buffer.data.at(bufferView.byteOffset), bufferView.byteLength); const bgfx::Memory* ibMem = bgfx::alloc(indexBufferView.byteLength);
bx::memCopy(ibMem->data, &indexBuffer.data.at(indexBufferView.byteOffset), indexBufferView.byteLength);
mesh.IndexBuffer = bgfx::createIndexBuffer(ibMem); mesh.IndexBuffer = bgfx::createIndexBuffer(ibMem);
}
{
tinygltf::Accessor posAccessor = model.accessors.at(primitive.attributes.at("POSITION")); tinygltf::Accessor posAccessor = model.accessors.at(primitive.attributes.at("POSITION"));
tinygltf::Accessor normalAccessor = model.accessors.at(primitive.attributes.at("NORMAL")); tinygltf::Accessor normalAccessor = model.accessors.at(primitive.attributes.at("NORMAL"));
tinygltf::Accessor uvAccessor = model.accessors.at(primitive.attributes.at("TEXCOORD_0")); tinygltf::Accessor uvAccessor = model.accessors.at(primitive.attributes.at("TEXCOORD_0"));
tinygltf::BufferView posBufferView = model.bufferViews[posAccessor.bufferView]; tinygltf::BufferView posBufferView = model.bufferViews[posAccessor.bufferView];
tinygltf::BufferView normalBufferView = model.bufferViews[normalAccessor.bufferView]; tinygltf::BufferView normalBufferView = model.bufferViews[normalAccessor.bufferView];
tinygltf::BufferView uvBufferView = model.bufferViews[uvAccessor.bufferView]; tinygltf::BufferView uvBufferView = model.bufferViews[uvAccessor.bufferView];
int posStride = posAccessor.ByteStride(posBufferView); int32_t posStride = posAccessor.ByteStride(posBufferView);
int normalStride = normalAccessor.ByteStride(normalBufferView); int32_t normalStride = normalAccessor.ByteStride(normalBufferView);
int uvStride = uvAccessor.ByteStride(uvBufferView); int32_t uvStride = uvAccessor.ByteStride(uvBufferView);
tinygltf::Buffer posBuffer = model.buffers[posBufferView.buffer]; tinygltf::Buffer posBuffer = model.buffers[posBufferView.buffer];
tinygltf::Buffer normalBuffer = model.buffers[normalBufferView.buffer]; tinygltf::Buffer normalBuffer = model.buffers[normalBufferView.buffer];
tinygltf::Buffer uvBuffer = model.buffers[uvBufferView.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); bx::memCopy(&v.uv_x, &uvBuffer.data.at(uvBufferView.byteOffset + i * uvStride), uvStride);
} }
mesh.VertexBuffer = bgfx::createVertexBuffer(vbMem, mesh.VertLayout); 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; return true;
} }
@@ -143,7 +254,7 @@ namespace Game
if (LoadMesh(mod, fullPath.getCPtr(), modelFileIsBinary[i])) if (LoadMesh(mod, fullPath.getCPtr(), modelFileIsBinary[i]))
{ {
mod.Handle.Asset.Idx = CrcPath(fullPath.getCPtr()); mod.Handle.Asset.Idx = CrcPath(fullPath.getCPtr());
mod.Handle.ModelIdx = inst.DebugData.AssetCount; mod.Handle.ModelIdx = writeI;
if (inst.DebugData.AssetCount < inst.DebugData.MaxAssets) if (inst.DebugData.AssetCount < inst.DebugData.MaxAssets)
{ {
inst.DebugData.AssetHandles[inst.DebugData.AssetCount] = mod.Handle.Asset; inst.DebugData.AssetHandles[inst.DebugData.AssetCount] = mod.Handle.Asset;

View File

@@ -1,6 +1,6 @@
#include "../gen/Def.h" #include "../gen/Def.h"
#include "Gen.h"
#include "Global.h" #include "Global.h"
#include "Instance.h"
#include "Log.h" #include "Log.h"
#include "Puzzle.h" #include "Puzzle.h"
@@ -8,25 +8,26 @@
#include "bx/string.h" #include "bx/string.h"
#include "imgui.h" #include "imgui.h"
#include <cassert> #include <cassert>
#include <cstdio>
namespace namespace
{ {
static constexpr Generated::PuzPos Dirs[4]{ using namespace Gen;
static constexpr PuzPos Dirs[4]{
{-1, 0}, {-1, 0},
{0, -1}, {0, -1},
{0, 1}, {0, 1},
{1, 0}, {1, 0},
}; };
Generated::StaticPuzzleData StaticData; StaticPuzzleData StaticData;
Generated::StaticPuzzleCard InvalidCard; StaticPuzzleCard InvalidCard;
PlacedPuzzleCard InvalidPlacedCard;
} // namespace } // namespace
namespace Puzzle namespace Puzzle
{ {
constexpr float UIPuzBoxSize = 26; constexpr float UIPuzBoxSize = 26;
using namespace Generated; using namespace Gen;
void Setup() void Setup()
{ {
@@ -103,6 +104,24 @@ namespace Puzzle
return puz.BackgroundTiles[pos.Y * Puzzle::Config::MaxPuzzleSizeTiles + pos.X]; return puz.BackgroundTiles[pos.Y * Puzzle::Config::MaxPuzzleSizeTiles + 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) PuzzleElementType::Enum GetCardNodeAt(const StaticPuzzleCard& card, uint8_t rotation, int8_t x, int8_t y)
{ {
assert(x >= 0 && x < Puzzle::Config::CardSize); assert(x >= 0 && x < Puzzle::Config::CardSize);
@@ -172,6 +191,11 @@ namespace Puzzle
bool PuzzleSolver::IsExitSatisfied(const PuzzleData& puzzle, PuzPos pos) bool PuzzleSolver::IsExitSatisfied(const PuzzleData& puzzle, PuzPos pos)
{ {
PuzzleElementType::Enum goalType = GetNodeAt(puzzle, pos); PuzzleElementType::Enum goalType = GetNodeAt(puzzle, pos);
if (goalType == PuzzleElementType::None)
{
WARN_ONCE("TODO!");
return false;
}
uint32_t currentPositionQueueIdx = 0; uint32_t currentPositionQueueIdx = 0;
uint32_t positionQueueCount = 0; uint32_t positionQueueCount = 0;
@@ -234,7 +258,7 @@ namespace Puzzle
return from == PuzzleElementType::ElectricIn || from == PuzzleElementType::ElectricGoal || return from == PuzzleElementType::ElectricIn || from == PuzzleElementType::ElectricGoal ||
from == PuzzleElementType::None; from == PuzzleElementType::None;
} }
assert(false); // assert(false);
return false; return false;
} }
bool PuzzleSolver::IsValidSource(PuzzleElementType::Enum sourceType, PuzzleElementType::Enum goalType) bool PuzzleSolver::IsValidSource(PuzzleElementType::Enum sourceType, PuzzleElementType::Enum goalType)
@@ -271,19 +295,47 @@ namespace Puzzle
ImGui::InvisibleButton("cardbn", ImGui::InvisibleButton("cardbn",
{Puzzle::Config::CardSize * UIPuzBoxSize, Puzzle::Config::CardSize * UIPuzBoxSize}); {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) void WritePuzzleFilePath(char* buf, int32_t bufSize, uint16_t puzID)
{ {
bx::snprintf(buf, bufSize, "%s/%u.pzl", Puzzle::PuzzleFileDir, puzID); bx::snprintf(buf, bufSize, "%s/%u.pzl", Puzzle::PuzzleFileDir, puzID);
} }
// TODO: targetPos is of type CardPos 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) bool ReturnPlacedCard(PuzzleData& obj, PuzPos targetPos)
{ {
auto& placedCard = obj.PlacedCards[targetPos.Y * Config::MaxPuzzleSizeCards + targetPos.X]; PlacedPuzzleCard& placedCard = GetCardAt(obj, targetPos);
if (IsValid(placedCard.RefCard)) if (IsValid(placedCard.RefCard))
{ {
if (placedCard.IsLocked) return false; if (GetFlag(placedCard.Flags, PlacedPuzzleCardFlags::Locked))
{
LOG_WARN("Card at %i %i is locked!", targetPos.X, targetPos.Y);
return false;
}
bool found = false; bool found = false;
for (int32_t i = 0; i < obj.AvailableCardCount; ++i) for (int32_t i = 0; i < obj.AvailableCardCount; ++i)
{ {
@@ -304,7 +356,6 @@ namespace Puzzle
return true; return true;
} }
// TODO: targetPos is of type CardPos
bool DragAvailableCardTo(PuzzleData& obj, PuzPos targetPos, int32_t availIdx, uint8_t rotation) bool DragAvailableCardTo(PuzzleData& obj, PuzPos targetPos, int32_t availIdx, uint8_t rotation)
{ {
if (availIdx >= obj.AvailableCardCount) if (availIdx >= obj.AvailableCardCount)
@@ -317,19 +368,55 @@ namespace Puzzle
auto& draggedCard = obj.AvailableCards[availIdx]; auto& draggedCard = obj.AvailableCards[availIdx];
draggedCard.UsedCount++; draggedCard.UsedCount++;
auto& placedCard = obj.PlacedCards[targetPos.Y * Config::MaxPuzzleSizeCards + targetPos.X]; PlacedPuzzleCard& placedCard = GetCardAt(obj, targetPos);
placedCard.RefCard = draggedCard.RefCard; placedCard.RefCard = draggedCard.RefCard;
placedCard.IsLocked = false; placedCard.Flags = (PlacedPuzzleCardFlags::Enum)ClearFlags(placedCard.Flags, PlacedPuzzleCardFlags::Locked);
placedCard.Position = targetPos; placedCard.Position = targetPos;
placedCard.Rotation = rotation; placedCard.Rotation = rotation;
return true; 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 RenderDebugUI(PuzzleData& obj)
{ {
bool dataChanged = false;
uint8_t debugRot = Game::GetInstance().DebugData.DebugCardRotation;
bool isVisible = true; bool isVisible = true;
if (ImGui::Begin("Puzzle", &isVisible)) if (ImGui::Begin("Puzzle", &isVisible))
{ {
@@ -337,23 +424,40 @@ namespace Puzzle
{ {
char filepath[128]{0}; char filepath[128]{0};
WritePuzzleFilePath(filepath, sizeof(filepath), obj.ID); WritePuzzleFilePath(filepath, sizeof(filepath), obj.ID);
remove(filepath); bx::remove(filepath);
obj.ID = UINT16_MAX; obj.ID = UINT16_MAX;
ImGui::End(); ImGui::End();
return false; return false;
} }
ImGui::SameLine(); 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; int32_t val = obj.ID;
if (ImGui::InputInt("ID", &val)) if (ImGui::InputInt("ID", &val))
{ {
obj.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 W = obj.WidthTiles;
int32_t H = obj.HeightTiles; int32_t H = obj.HeightTiles;
@@ -362,7 +466,6 @@ namespace Puzzle
if (ImGui::DragInt("", &W, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles)) if (ImGui::DragInt("", &W, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
{ {
obj.WidthTiles = uint8_t(W); obj.WidthTiles = uint8_t(W);
dataChanged = true;
} }
ImGui::PopID(); ImGui::PopID();
ImGui::SameLine(); ImGui::SameLine();
@@ -373,7 +476,6 @@ namespace Puzzle
if (ImGui::DragInt("", &H, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles)) if (ImGui::DragInt("", &H, 0.3f, 0, Puzzle::Config::MaxPuzzleSizeTiles))
{ {
obj.HeightTiles = uint8_t(H); obj.HeightTiles = uint8_t(H);
dataChanged = true;
} }
ImGui::PopID(); ImGui::PopID();
@@ -388,7 +490,7 @@ namespace Puzzle
{ {
ImGui::PushID(x); ImGui::PushID(x);
PuzPos nodePos = {int8_t(x), int8_t(y)}; PuzPos nodePos = {int8_t(x), int8_t(y)};
auto node = GetNodeAt(obj, nodePos); auto node = GetInitialNodeAt(obj, nodePos);
if (node == PuzzleElementType::WaterGoal) if (node == PuzzleElementType::WaterGoal)
{ {
obj.GoalPositions[obj.GoalPositionCount] = nodePos; obj.GoalPositions[obj.GoalPositionCount] = nodePos;
@@ -401,36 +503,30 @@ namespace Puzzle
ImGui::SetCursorScreenPos(pos); ImGui::SetCursorScreenPos(pos);
if (x % Puzzle::Config::CardSize == 0 && y % Puzzle::Config::CardSize == 0) if (x % Puzzle::Config::CardSize == 0 && y % Puzzle::Config::CardSize == 0)
{ {
int32_t cardX = x / Config::CardSize; PuzPos cardPos = {int8_t(x / Config::CardSize), int8_t(y / Config::CardSize)};
int32_t cardY = y / Config::CardSize; PlacedPuzzleCard& placedCard = GetInitialCardAt(obj, cardPos);
PuzPos cardPos = {int8_t(cardX), int8_t(cardY)}; bool isLocked = GetFlag(placedCard.Flags, PlacedPuzzleCardFlags::Locked);
auto& placedCard = obj.PlacedCards[cardY * Config::MaxPuzzleSizeCards + cardX];
ImGui::InvisibleButton("bn", {UIPuzBoxSize * 2, UIPuzBoxSize * 2}); ImGui::InvisibleButton("bn", {UIPuzBoxSize * 2, UIPuzBoxSize * 2});
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) if (ImGui::IsItemClicked(ImGuiMouseButton_Right))
{ {
placedCard.Rotation += 1; placedCard.Rotation += 1;
if (placedCard.Rotation >= 4) placedCard.Rotation = 0; if (placedCard.Rotation >= 4) placedCard.Rotation = 0;
dataChanged = true;
} }
if (ImGui::IsItemClicked(ImGuiMouseButton_Middle) && IsValid(placedCard.RefCard)) if (ImGui::IsItemClicked(ImGuiMouseButton_Middle))
{ {
if (placedCard.IsLocked) if (ImGui::IsKeyDown(ImGuiKey_LeftShift))
{ {
placedCard.IsLocked = false; placedCard = {};
placedCard.RefCard = {};
dataChanged = true;
} }
else else
{ {
if (!ReturnPlacedCard(obj, cardPos)) placedCard.Flags =
{ (PlacedPuzzleCardFlags::Enum)(placedCard.Flags ^ PlacedPuzzleCardFlags::Locked);
placedCard.IsLocked = false; RecalculateInitialAvailable(obj);
placedCard.RefCard = {};
dataChanged = true;
}
} }
} }
if (!placedCard.IsLocked && IsValid(placedCard.RefCard)) if (!isLocked && IsValid(placedCard.RefCard))
{ {
ImVec2 s = {puzCursorStart.x + x * UIPuzBoxSize, puzCursorStart.y + y * UIPuzBoxSize}; ImVec2 s = {puzCursorStart.x + x * UIPuzBoxSize, puzCursorStart.y + y * UIPuzBoxSize};
drawList.AddRectFilled(s, drawList.AddRectFilled(s,
@@ -445,17 +541,11 @@ namespace Puzzle
uint32_t CardIdx = *reinterpret_cast<uint32_t*>(payload->Data); uint32_t CardIdx = *reinterpret_cast<uint32_t*>(payload->Data);
placedCard = {}; placedCard = {};
placedCard.RefCard = {(uint16_t)CardIdx}; placedCard.RefCard = {(uint16_t)CardIdx};
placedCard.Rotation = debugRot; placedCard.Rotation = 0;
placedCard.Position = cardPos; placedCard.Position = cardPos;
placedCard.IsLocked = true; placedCard.Flags = (PlacedPuzzleCardFlags::Enum)SetFlags(placedCard.Flags,
dataChanged = true; PlacedPuzzleCardFlags::Locked);
} RecalculateInitialAvailable(obj);
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("availcard", 0))
{
DragAvailableCardTo(obj,
cardPos,
*(int32_t*)payload->Data,
Game::GetInstance().DebugData.DebugCardRotation);
} }
ImGui::EndDragDropTarget(); ImGui::EndDragDropTarget();
} }
@@ -521,7 +611,6 @@ namespace Puzzle
obj.AvailableCardCount++; obj.AvailableCardCount++;
} }
} }
dataChanged = true;
} }
ImGui::EndDragDropTarget(); ImGui::EndDragDropTarget();
} }
@@ -534,14 +623,8 @@ namespace Puzzle
ImGui::SetCursorScreenPos(localPos); ImGui::SetCursorScreenPos(localPos);
ImGui::PushID(i); ImGui::PushID(i);
auto& card = obj.AvailableCards[i]; auto& card = obj.AvailableCards[i];
DrawCard(GetCard(card.RefCard), debugRot, ImGui::GetCursorScreenPos()); DrawCard(GetCard(card.RefCard), 0, ImGui::GetCursorScreenPos());
int displayCount = card.MaxAvailableCount - card.UsedCount; int displayCount = card.MaxAvailableCount - card.UsedCount;
if (displayCount > 0 && ImGui::BeginDragDropSource())
{
ImGui::SetDragDropPayload("availcard", &i, sizeof(i));
DrawCard(GetCard(card.RefCard), debugRot, ImGui::GetCursorScreenPos());
ImGui::EndDragDropSource();
}
ImGui::SetCursorScreenPos({localPos.x, localPos.y + 55.0f}); ImGui::SetCursorScreenPos({localPos.x, localPos.y + 55.0f});
ImGui::SetNextItemWidth(35); ImGui::SetNextItemWidth(35);
@@ -550,7 +633,6 @@ namespace Puzzle
{ {
int diff = displayCount - (card.MaxAvailableCount - card.UsedCount); int diff = displayCount - (card.MaxAvailableCount - card.UsedCount);
card.MaxAvailableCount = bx::max(0, card.MaxAvailableCount + diff); card.MaxAvailableCount = bx::max(0, card.MaxAvailableCount + diff);
dataChanged = true;
} }
ImGui::PopID(); ImGui::PopID();
ImGui::SameLine(0, 3); ImGui::SameLine(0, 3);
@@ -569,22 +651,17 @@ namespace Puzzle
} }
ImGui::End(); ImGui::End();
if (dataChanged)
{
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();
}
return isVisible; 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 Puzzle

View File

@@ -2,26 +2,30 @@
#include <cstdint> #include <cstdint>
#include <imgui.h> #include <imgui.h>
#include "Gen.h" #include "../gen/Generated.h"
namespace Puzzle namespace Puzzle
{ {
using namespace Generated; using namespace Gen;
constexpr const char* PuzzleFileDir = "game/data/puzzles/"; constexpr const char* PuzzleFileDir = "game/data/puzzles/";
struct Config struct Config
{ {
static constexpr uint32_t MaxVisiblePuzzles = 3;
static constexpr uint32_t CardSize = 2; static constexpr uint32_t CardSize = 2;
static constexpr uint32_t NodesPerCard = CardSize * CardSize; static constexpr uint32_t NodesPerCard = CardSize * CardSize;
static constexpr uint32_t MaxElementsPerTile = 4;
static constexpr uint32_t MaxPuzzleSizeCards = 16; static constexpr uint32_t MaxPuzzleSizeCards = 16;
static constexpr uint32_t MaxCardsInPuzzle = MaxPuzzleSizeCards * MaxPuzzleSizeCards; static constexpr uint32_t MaxCardsInPuzzle = MaxPuzzleSizeCards * MaxPuzzleSizeCards;
static constexpr uint32_t MaxPuzzleSizeTiles = 16 * CardSize; static constexpr uint32_t MaxPuzzleSizeTiles = 16 * CardSize;
static constexpr uint32_t MaxTilesInPuzzle = MaxPuzzleSizeTiles * MaxPuzzleSizeTiles; 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 MaxAvailableStacks = 16;
static constexpr uint32_t MaxGoalPositions = 16; static constexpr uint32_t MaxGoalPositions = 16;
static constexpr float CardScaleWorld = 10.0f; static constexpr float CardScaleWorld = 10.0f;
static constexpr uint32_t MaxCoversInTile = 8;
static constexpr uint32_t MaxCoversTotal = MaxCoversInTile * MaxTilesTotal;
}; };
void Setup(); void Setup();
@@ -32,17 +36,27 @@ namespace Puzzle
bool IsValid(StaticPuzzleCardHandle h); bool IsValid(StaticPuzzleCardHandle h);
uint8_t GetRemainingCount(const PuzzleCardStack& stack); uint8_t GetRemainingCount(const PuzzleCardStack& stack);
PuzzleElementType::Enum GetNodeAt(const PuzzleData& puz, PuzPos pos); 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 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); 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 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 struct PuzzleSolver
{ {
bool IsPuzzleSolved(const Generated::PuzzleData& puzzle); bool IsPuzzleSolved(const PuzzleData& puzzle);
bool IsExitSatisfied(const Generated::PuzzleData& puzzle, PuzPos pos); bool IsExitSatisfied(const PuzzleData& puzzle, PuzPos pos);
// This assumes flowFrom is already verified to be connected. // This assumes flowFrom is already verified to be connected.
bool IsValidGoalConnection(const Generated::PuzzleData& puzzle, bool IsValidGoalConnection(const PuzzleData& puzzle,
PuzPos flowFrom, PuzPos flowFrom,
PuzPos flowTo, PuzPos flowTo,
PuzzleElementType::Enum goalType); PuzzleElementType::Enum goalType);

View File

@@ -1,8 +1,8 @@
#include "Global.h" #include "Global.h"
#include "Input.h"
#include "Instance.h" #include "Instance.h"
#include "Log.h" #include "Log.h"
#include "Setup.h" #include "Setup.h"
#include "Tools.h"
#include "rendering/Rendering.h" #include "rendering/Rendering.h"
#include "bx/bx.h" #include "bx/bx.h"
@@ -33,32 +33,32 @@ namespace Game
tracy::StartupProfiler(); tracy::StartupProfiler();
#endif #endif
if (shared.Game.PermanentStorage == nullptr) if (shared.Game.PermanentArena.Base == nullptr)
{ {
LOG_ERROR("Game memory not initialized!!"); LOG_ERROR("Game memory not initialized!!");
return; return;
} }
if (shared.Game.EntityStorage == nullptr) if (shared.Game.EntityArena.Base == nullptr)
{ {
LOG_ERROR("Entity memory not initialized!"); LOG_ERROR("Entity memory not initialized!");
return; 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; return;
} }
GameInstance& instance = *reinterpret_cast<GameInstance*>(shared.Game.PermanentStorage); GameInstance& instance = *reinterpret_cast<GameInstance*>(shared.Game.PermanentArena.Base);
if (sizeof(GameInstance) != instance.Size) if (sizeof(GameInstance) != instance.Size)
{ {
LOG_WARN("Game instance size changed, resetting!"); LOG_WARN("Game instance size changed, resetting!");
instance = {}; instance = {};
} }
instance.UsedScratchAmount = 0;
SetShared(shared); SetShared(shared);
SetInstance(instance); SetInstance(instance);
ResetScratch();
Puzzle::LoadStaticPuzzleData(); Puzzle::LoadStaticPuzzleData();
SetupInstance.Rendering.Setup(); SetupInstance.Rendering.Setup({});
instance.GameLevel.Setup(shared.Game); instance.GameLevel.Setup(shared.Game);
instance.IsInitialized = true; instance.IsInitialized = true;
} }
@@ -87,6 +87,7 @@ namespace Game
bx::memCopy(win.LastHeldScanCodes, win.HeldScanCodes, sizeof(win.HeldScanCodes)); bx::memCopy(win.LastHeldScanCodes, win.HeldScanCodes, sizeof(win.HeldScanCodes));
bx::memCopy(win.LastHeldMouseButtons, win.HeldMouseButtons, sizeof(win.HeldMouseButtons)); bx::memCopy(win.LastHeldMouseButtons, win.HeldMouseButtons, sizeof(win.HeldMouseButtons));
} }
Tools::MeasureFrameEnd();
FrameMark; FrameMark;
} }

View File

@@ -1,13 +1,32 @@
#include "../gen/Def.h"
#include "Gen.h"
#include "Global.h" #include "Global.h"
#include "Instance.h" #include "Instance.h"
#include "Log.h"
#include "Mesh.h"
#include "Puzzle.h"
#include "Tools.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 <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 namespace Tools
{ {
const char* GetAssetPath(Generated::AssetHandle assetHandle) const char* GetAssetPath(Gen::AssetHandle assetHandle)
{ {
const auto& inst = Game::GetInstance(); const auto& inst = Game::GetInstance();
for (int32_t j = 0; j < inst.DebugData.AssetCount; ++j) for (int32_t j = 0; j < inst.DebugData.AssetCount; ++j)
@@ -20,20 +39,722 @@ namespace Tools
return "---"; return "---";
} }
void ModelDropdown(Generated::ModelHandle& 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(); auto& R = Game::GameRendering::Get();
const char* name = GetAssetPath(modelHandle.Asset); const char* assetName = GetAssetPath(modelHandle.Asset);
if (ImGui::BeginCombo("Models", name)) if (ImGui::BeginCombo(title, assetName))
{ {
for (int32_t i = 0; i < R.ModelCount; ++i) for (int32_t i = 0; i < R.ModelCount; ++i)
{ {
if (ImGui::Selectable(GetAssetPath(R.Models[i].Handle.Asset), i == modelHandle.ModelIdx)) if (ImGui::Selectable(GetAssetPath(R.Models[i].Handle.Asset), i == modelHandle.ModelIdx))
{ {
modelHandle = R.Models[i].Handle; modelHandle = R.Models[i].Handle;
changed = true;
} }
} }
ImGui::EndCombo(); 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 } // namespace Tools

View File

@@ -1,7 +1,14 @@
#pragma once #pragma once
#include "Gen.h" #include "../gen/Generated.h"
#include "rendering/Rendering.h"
namespace Tools namespace Tools
{ {
void ModelDropdown(Generated::ModelHandle& 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 } // 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 type Vec2
{ {
f32 X f32 x
f32 Y f32 y
} }
type Vec3 type Vec3
{ {
f32 X f32 x
f32 Y f32 y
f32 Z f32 z
} }
type Vec4 type Vec4
{ {
f32 X f32 x
f32 Y f32 y
f32 Z f32 z
f32 W f32 w
} }
type Mat3 type Mat3
@@ -38,14 +38,29 @@ type Mat4
}") }")
} }
type Transform
{
Mat4 M
Mat4 MI
Vec3 Position
Mat4 Rotation
Vec3 Scale Default("{1.0f, 1.0f, 1.0f}")
}
type AssetHandle type AssetHandle
{ {
u32 Idx Default("UINT32_MAX") u32 Idx Default("UINT32_MAX")
} }
type ModelHandle type ModelHandle
{ {
u16 ModelIdx Default("UINT16_MAX") u16 ModelIdx Default("UINT16_MAX")
AssetHandle Asset
}
type TextureHandle
{
u16 TextureIdx Default("UINT16_MAX")
AssetHandle Asset AssetHandle Asset
} }
@@ -67,11 +82,23 @@ enum PuzzleElementType(u8)
Bridge GameName("Bridge") ShortName("#") Bridge GameName("Bridge") ShortName("#")
} }
type CardSocket
{
ModelHandle Model
u8 ConnectionDirection
}
type StaticPuzzleCard type StaticPuzzleCard
{ {
PuzzleElementType Elements Arr(4) PuzzleElementType Elements Arr(4)
ModelHandle ModelHandle ModelHandle BaseModelHandle
ModelHandle NorthCoverHandle
ModelHandle EastCoverHandle
ModelHandle SouthCoverHandle
ModelHandle WestCoverHandle
CardSocket Sockets Arr(16)
TextureHandle ModelTextureHandle
TextureHandle BoardTextureHandle
} }
type StaticPuzzleCardHandle type StaticPuzzleCardHandle
@@ -79,9 +106,18 @@ type StaticPuzzleCardHandle
u16 Idx Default("UINT16_MAX") u16 Idx Default("UINT16_MAX")
} }
type PuzzleVisualSettings
{
Vec4 TileBaseColor
Vec4 TileDotColor
Vec3 Test
Vec4 DisabledCardTint
}
type StaticPuzzleData type StaticPuzzleData
{ {
StaticPuzzleCard Cards Arr(64) StaticPuzzleCard Cards Arr(64)
PuzzleVisualSettings Visuals
} }
type PuzzleCardStack type PuzzleCardStack
@@ -91,12 +127,18 @@ type PuzzleCardStack
u8 UsedCount u8 UsedCount
} }
enum PlacedPuzzleCardFlags(u8) Flags
{
None
Locked
}
type PlacedPuzzleCard type PlacedPuzzleCard
{ {
StaticPuzzleCardHandle RefCard StaticPuzzleCardHandle RefCard
PuzPos Position PuzPos Position
u8 Rotation u8 Rotation
b IsLocked PlacedPuzzleCardFlags Flags
} }
type PuzzleData type PuzzleData
@@ -108,7 +150,35 @@ type PuzzleData
u32 AvailableCardCount u32 AvailableCardCount
PuzzleCardStack AvailableCards Arr(16) PuzzleCardStack AvailableCards Arr(16)
PlacedPuzzleCard PlacedCards Arr(256) PlacedPuzzleCard PlacedCards Arr(256)
PlacedPuzzleCard InitialPlacedCards Arr(256)
PuzzleElementType BackgroundTiles Arr(1024) PuzzleElementType BackgroundTiles Arr(1024)
u32 GoalPositionCount u32 GoalPositionCount
PuzPos GoalPositions Arr(16) 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 "../Tools.h"
#include "Rendering.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 "backends/imgui_impl_sdl3.h"
#include "bgfx/defines.h" #include "bgfx/defines.h"
#include "bx/bx.h" #include "bx/bx.h"
#include "bx/constants.h" #include "bx/debug.h"
#include "bx/filepath.h" #include "bx/filepath.h"
#include "bx/math.h"
#include "bx/string.h" #include "bx/string.h"
#include "bx/timer.h" #include "bx/timer.h"
#include <bgfx/bgfx.h> #include <bgfx/bgfx.h>
@@ -33,15 +33,105 @@ namespace Game
{ {
namespace 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) const bgfx::Memory* loadFile(const char* path, bool appendZero = false, int32_t retryCount = 1)
{ {
FILE* file; FILE* file;
for (int32_t i = 0; i < retryCount; ++i) for (int32_t i = 0; i < retryCount; ++i)
{ {
file = fopen(path, "rb"); file = std::fopen(path, "rb");
if (file == nullptr && i < retryCount - 1) if (file == nullptr && i < retryCount - 1)
{ {
std::this_thread::sleep_for(100ms); std::this_thread::sleep_for(100ms);
LOG_WARN("Failed to open file, retrying...");
break; break;
} }
@@ -49,8 +139,13 @@ namespace Game
long fileSize = ftell(file); long fileSize = ftell(file);
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
long fileSizeX = appendZero ? fileSize + 1 : fileSize; long fileSizeX = appendZero ? (fileSize + 1) : fileSize;
void* rawMem = AllocateScratch(fileSizeX); 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); const bgfx::Memory* mem = bgfx::makeRef(rawMem, fileSizeX);
fread(mem->data, 1, fileSize, file); fread(mem->data, 1, fileSize, file);
if (appendZero) if (appendZero)
@@ -62,6 +157,7 @@ namespace Game
return mem; return mem;
} }
LOG_WARN("File inaccessible! %s", path);
return nullptr; return nullptr;
} }
@@ -123,8 +219,8 @@ namespace Game
bgfx::TextureHandle handle = BGFX_INVALID_HANDLE; bgfx::TextureHandle handle = BGFX_INVALID_HANDLE;
bx::Error err; bx::Error err;
const bgfx::Memory* data = loadFile(_filePath.getCPtr()); const bgfx::Memory* data = LoadBinaryFile(_filePath.getCPtr());
if (data == nullptr) if (data == nullptr || data->data == nullptr || data->size == 0)
{ {
LOG_WARN("Failed to find image %s", _filePath.getCPtr()); LOG_WARN("Failed to find image %s", _filePath.getCPtr());
return handle; return handle;
@@ -140,8 +236,18 @@ namespace Game
*_orientation = imageContainer.m_orientation; *_orientation = imageContainer.m_orientation;
} }
const bgfx::Memory* mem = // We're working with ktx textures, so the size should be in front of the actual data
bgfx::makeRef(data->data + imageContainer.m_offset, data->size - imageContainer.m_offset); 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) if (NULL != _info)
{ {
@@ -198,130 +304,6 @@ namespace Game
return handle; 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; GameRendering* Instance = nullptr;
} // namespace } // namespace
@@ -331,19 +313,24 @@ namespace Game
return *Instance; return *Instance;
} }
void GameRendering::Setup() void GameRendering::Setup(const RenderingSetup& setup)
{ {
LOG("--- RENDERING STARTUP ---"); LOG("--- RENDERING STARTUP ---");
ZoneScopedN("Setup"); ZoneScopedN("Setup");
SetupData = setup;
if (Instance != nullptr) LOG_WARN("old rendering wasn't destroyed!"); if (Instance != nullptr) LOG_WARN("old rendering wasn't destroyed!");
Instance = this; Instance = this;
SharedData& shared = GetShared(); SharedData& shared = GetShared();
bgfx::Init init; bgfx::Init init;
init.type = bgfx::RendererType::Direct3D12; init.type = bgfx::RendererType::Direct3D11;
#ifdef _DEBUG #ifdef _DEBUG
// init.debug = true; init.debug = true;
// init.debug = false;
#else
init.debug = false;
#endif #endif
init.platformData.nwh = shared.Window.Handle; init.platformData.nwh = shared.Window.Handle;
init.platformData.ndt = nullptr; init.platformData.ndt = nullptr;
@@ -365,35 +352,34 @@ namespace Game
{ {
LOG("BGFX setup succeded!"); LOG("BGFX setup succeded!");
} }
// bgfx::setDebug(BGFX_DEBUG_TEXT);
bgfx::setViewClear(MainViewID, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x3399FFff, 1.0f, 0); bgfx::setViewClear(MainViewID, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x3399FFff, 1.0f, 0);
bgfx::setViewRect(MainViewID, 0, 0, shared.Window.WindowWidth, shared.Window.WindowHeight); bgfx::setViewRect(MainViewID, 0, 0, shared.Window.WindowWidth, shared.Window.WindowHeight);
DefaultSampler = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler); DefaultSampler = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler);
Textures[0].Handle = loadTexture( LoadTextures();
bx::FilePath{"models/body.dds"}, BGFX_TEXTURE_NONE | BGFX_SAMPLER_NONE, 0, &Textures[0].Info, nullptr);
Textures[0].SamplerHandle = DefaultSampler;
LoadModels(Models, ModelCount); LoadModels(Models, ModelCount);
Materials[0] = ReloadShaders();
Material::LoadFromShader("vert", "frag", MainViewID, Textures[0].Handle, Textures[0].SamplerHandle);
imguiCreate();
SetImguiStyle();
if (!ImGui_ImplSDL3_InitForOther(shared.Window.SDLWindow)) if (SetupData.UseImgui)
{ {
LOG_ERROR("Failed to set up imgui implementation!"); imguiCreate();
return; SetImguiStyle();
} if (!ImGui_ImplSDL3_InitForOther(shared.Window.SDLWindow))
{
LOG_ERROR("Failed to set up imgui implementation!");
return;
}
// ImGui::GetIO().BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // ImGui::GetIO().BackendFlags |= ImGuiBackendFlags_RendererHasViewports;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable; ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable;
// ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
// auto& platIO = ImGui::GetPlatformIO(); // auto& platIO = ImGui::GetPlatformIO();
// platIO.Renderer_CreateWindow = TODO; // platIO.Renderer_CreateWindow = TODO;
// platIO.Platform_DestroyWindow = TODO; // platIO.Platform_DestroyWindow = TODO;
// platIO.Platform_SetWindowSize = TODO; // platIO.Platform_SetWindowSize = TODO;
// platIO.Platform_RenderWindow = TODO; // platIO.Platform_RenderWindow = TODO;
}
GameInstance& inst = GetInstance(); GameInstance& inst = GetInstance();
if (!inst.IsInitialized) if (!inst.IsInitialized)
@@ -401,13 +387,16 @@ namespace Game
inst.Time.StartTime = bx::getHPCounter(); inst.Time.StartTime = bx::getHPCounter();
} }
if (inst.DebugData.ImguiIniSize > 0) if (SetupData.UseImgui)
{ {
ImGui::LoadIniSettingsFromMemory(inst.DebugData.ImguiIni, inst.DebugData.ImguiIniSize); if (inst.DebugData.ImguiIniSize > 0)
} {
else ImGui::LoadIniSettingsFromMemory(inst.DebugData.ImguiIni, inst.DebugData.ImguiIniSize);
{ }
ImGui::LoadIniSettingsFromDisk("imgui.ini"); else
{
ImGui::LoadIniSettingsFromDisk("imgui.ini");
}
} }
DitherGen(DitherTextures, DitherRecursion); DitherGen(DitherTextures, DitherRecursion);
} }
@@ -419,7 +408,10 @@ namespace Game
for (uint16_t i = 0; i < shared.Window.SDLEventCount; ++i) for (uint16_t i = 0; i < shared.Window.SDLEventCount; ++i)
{ {
ImGui_ImplSDL3_ProcessEvent(&shared.Window.SDLEvents[i]); if (SetupData.UseImgui)
{
ImGui_ImplSDL3_ProcessEvent(&shared.Window.SDLEvents[i]);
}
} }
shared.Window.SDLEventCount = 0; shared.Window.SDLEventCount = 0;
@@ -437,228 +429,98 @@ namespace Game
FileChangeNotification* shaderChange = nullptr; FileChangeNotification* shaderChange = nullptr;
if (shared.Dev.ChangedShaderCount > 0) 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; shared.Dev.ChangedShaderCount = 0;
// TODO: when to destroy shader? // TODO: when to destroy shader?
// TODO: only reload changed shaders // TODO: only reload changed shaders
bgfx::ShaderHandle vertexShader = loadShader("vert"); ReloadShaders();
bgfx::ShaderHandle fragmentShader = loadShader("frag"); }
if (isValid(vertexShader) && isValid(fragmentShader)) }
void GameRendering::LoadTextures()
{
for (int32_t i = 0; i < MaxTextures; ++i)
{
Textures[i] = {};
}
bx::Error err;
bx::DirectoryReader reader{};
if (!reader.open("textures", &err) || !err.isOk())
{
LOG_ERROR("Failed to read textures dir: %s", err.getMessage());
}
bx::FileInfo info;
int32_t textureFilePathCount = 0;
bx::FilePath textureFilePaths[GameRendering::MaxTextures];
while (err.isOk())
{
int32_t res = reader.read(&info, sizeof(info), &err);
if (res == 0) break; // EOF
if (res != sizeof(info))
{ {
bgfx::ProgramHandle newProgram = bgfx::createProgram(vertexShader, fragmentShader, true); LOG_ERROR("Dir iter error: %s", err.getMessage());
if (isValid(newProgram)) break;
}
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)
{
if (textureFilePathCount >= GameRendering::MaxTextures)
{ {
Materials[0].Shader = newProgram; LOG_ERROR("Texture limit reached!");
LastShaderLoadTime = GetInstance().Time.Now; break;
}
else
{
LOG_WARN("Failed to load shader!");
LastShaderLoadTime = -1.0f;
} }
textureFilePaths[textureFilePathCount] = info.filePath;
textureFilePathCount++;
}
}
LOG("Found %u textures!", textureFilePathCount);
for (int32_t i = 0; i < textureFilePathCount; ++i)
{
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;
} }
} }
} }
void GameRendering::RenderDebugUI() void GameRendering::ReloadShaders()
{ {
ZoneScopedN("DebugUI"); Materials[Gen::EMaterial::Default] = Material::LoadFromShader("dither/vert", "dither/frag", MainViewID);
Materials[Gen::EMaterial::UI] = Material::LoadFromShader("normal/vert", "normal/frag", MainViewID);
auto& shared = GetShared();
auto& debug = GetInstance().DebugData;
auto& level = GetInstance().GameLevel;
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
{
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;
}
}
ImGui::Text("Shader log:");
ImGui::TextWrapped("%s", GetShared().Dev.ShaderLog);
}
ImGui::End();
if (ImGui::Begin("Puzzles"))
{
if (ImGui::Button("Add"))
{
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;
break;
}
}
if (!found)
{
LOG_ERROR("Too many puzzles!");
}
}
ImGui::Separator();
for (int32_t i = 0; i < BX_COUNTOF(level.Puzzles); ++i)
{
auto& puzzleData = level.Puzzles[i].Data;
if (puzzleData.ID == UINT16_MAX) continue;
bool isSelected = debug.SelectedDebugLevel == i;
ImGui::PushID("selectable");
if (ImGui::Selectable(puzzleData.PuzzleName, isSelected))
{
debug.SelectedDebugLevel = isSelected ? UINT16_MAX : i;
}
ImGui::PopID();
}
}
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)
{
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();
} }
void GameRendering::Update() void GameRendering::Update()
{ {
ZoneScopedN("Rendering"); ZoneScopedN("Rendering");
SharedData& shared = GetShared(); SharedData& shared = GetShared();
auto& time = GetInstance().Time;
HandleEvents(); HandleEvents();
// Start Rendering // Start Rendering
if (SetupData.UseImgui)
{ {
ZoneScopedN("Imgui Start Frame"); ZoneScopedN("Imgui Start Frame");
imguiBeginFrame(20); imguiBeginFrame(20);
@@ -666,21 +528,19 @@ namespace Game
ImGui::DockSpaceOverViewport(0, 0, ImGuiDockNodeFlags_PassthruCentralNode); ImGui::DockSpaceOverViewport(0, 0, ImGuiDockNodeFlags_PassthruCentralNode);
} }
if (UIVisible == UIVisibilityState::Debug) Tools::RenderDebugUI(*this);
{
RenderDebugUI();
}
GetInstance().GameLevel.Update(); GetInstance().GameLevel.Update();
GetInstance().GameLevel.Render(MainViewID, Models, Materials); GetInstance().GameLevel.Render(MainViewID, Models, Materials, Textures);
// Finish Frame // Finish Frame
if (SetupData.UseImgui)
{ {
ZoneScopedN("Imgui End Frame"); ZoneScopedN("Imgui End Frame");
imguiEndFrame(); imguiEndFrame();
} }
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) if (SetupData.UseImgui && ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{ {
ZoneScopedN("Imgui Platform Update"); ZoneScopedN("Imgui Platform Update");
ImGui::UpdatePlatformWindows(); ImGui::UpdatePlatformWindows();
@@ -699,19 +559,39 @@ namespace Game
{ {
ZoneScopedN("Shutdown"); ZoneScopedN("Shutdown");
LOG("--- RENDERING_SHUTDOWN ---"); LOG("--- RENDERING_SHUTDOWN ---");
ImGui::SaveIniSettingsToDisk("imgui.ini");
auto& debug = GetInstance().DebugData; for (int32_t i = 0; i < BX_COUNTOF(Textures); ++i)
const char* iniData = ImGui::SaveIniSettingsToMemory(reinterpret_cast<uint64_t*>(&debug.ImguiIniSize)); {
assert(debug.ImguiIniSize <= BX_COUNTOF(InstanceDebugData::ImguiIni)); if (isValid(Textures[i].RenderHandle))
bx::memCopy(debug.ImguiIni, iniData, bx::min(debug.ImguiIniSize, BX_COUNTOF(InstanceDebugData::ImguiIni))); {
ImGui_ImplSDL3_Shutdown(); bgfx::destroy(Textures[i].RenderHandle);
imguiDestroy(); 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));
assert(debug.ImguiIniSize <= BX_COUNTOF(InstanceDebugData::ImguiIni));
bx::memCopy(debug.ImguiIni, iniData, bx::min(debug.ImguiIniSize, BX_COUNTOF(InstanceDebugData::ImguiIni)));
ImGui_ImplSDL3_Shutdown();
imguiDestroy();
}
bgfx::shutdown(); bgfx::shutdown();
Instance = nullptr; Instance = nullptr;
} }
Material Material::LoadFromShader( Material Material::LoadFromShader(const char* vertPath, const char* fragPath, uint16_t view)
const char* vertPath, const char* fragPath, uint16_t view, bgfx::TextureHandle tex, bgfx::UniformHandle sampler)
{ {
BX_ASSERT(vertPath != nullptr && fragPath != nullptr, "Invalid shader path!"); BX_ASSERT(vertPath != nullptr && fragPath != nullptr, "Invalid shader path!");
bgfx::ShaderHandle vertexShader = loadShader(vertPath); bgfx::ShaderHandle vertexShader = loadShader(vertPath);
@@ -722,16 +602,14 @@ namespace Game
mat.State = 0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS | 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; BGFX_STATE_CULL_CCW | BGFX_STATE_MSAA;
mat.Uniforms[Material::UTime] = bgfx::createUniform("u_time", bgfx::UniformType::Vec4); 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::UTexInfo] = bgfx::createUniform("u_texInfo", bgfx::UniformType::Vec4);
mat.Uniforms[Material::UBaseColor] = bgfx::createUniform("u_baseColor", 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; mat.ViewID = view;
return mat; return mat;
} }
Generated::ModelHandle GameRendering::GetModelHandleFromPath(const char* path) Gen::ModelHandle GameRendering::GetModelHandleFromPath(const char* path)
{ {
uint32_t AssetHandle = CrcPath(path); uint32_t AssetHandle = CrcPath(path);
for (int32_t i = 0; i < ModelCount; ++i) for (int32_t i = 0; i < ModelCount; ++i)
@@ -741,6 +619,7 @@ namespace Game
return Models[i].Handle; return Models[i].Handle;
} }
} }
LOG_WARN("Failed to find model for path %s", path);
return {}; return {};
} }

View File

@@ -1,12 +1,11 @@
#pragma once #pragma once
#include "bgfx/defines.h" #include "bgfx/defines.h"
#include "imgui.h"
#include <bgfx/bgfx.h> #include <bgfx/bgfx.h>
#include <bx/string.h> #include <bx/string.h>
#include <cstdint> #include <cstdint>
#include "../Gen.h" #include "../../gen/Generated.h"
#include "../Global.h" #include "Dither.h"
union SDL_Event; union SDL_Event;
@@ -27,17 +26,42 @@ namespace Game
struct Texture struct Texture
{ {
bgfx::UniformHandle SamplerHandle; bgfx::UniformHandle SamplerHandle = {bgfx::kInvalidHandle};
bgfx::TextureHandle Handle; bgfx::TextureHandle RenderHandle = {bgfx::kInvalidHandle};
bgfx::TextureInfo Info; 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 struct Model
{ {
bgfx::VertexBufferHandle VertexBuffer; static constexpr uint16_t MaxSocketCount = 16;
bgfx::IndexBufferHandle IndexBuffer; bgfx::VertexBufferHandle VertexBuffer = {bgfx::kInvalidHandle};
bgfx::IndexBufferHandle IndexBuffer = {bgfx::kInvalidHandle};
bgfx::VertexLayout VertLayout; bgfx::VertexLayout VertLayout;
Generated::ModelHandle Handle; 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 struct Material
@@ -52,14 +76,9 @@ namespace Game
bgfx::ProgramHandle Shader; bgfx::ProgramHandle Shader;
bgfx::UniformHandle Uniforms[8]; bgfx::UniformHandle Uniforms[8];
Texture Textures[4];
uint64_t State = 0; uint64_t State = 0;
uint32_t ViewID = 0; uint32_t ViewID = 0;
static Material LoadFromShader(const char* vertPath, static Material LoadFromShader(const char* vertPath, const char* fragPath, uint16_t view);
const char* fragPath,
uint16_t view,
bgfx::TextureHandle = BGFX_INVALID_HANDLE,
bgfx::UniformHandle sampler = BGFX_INVALID_HANDLE);
}; };
enum class UIVisibilityState enum class UIVisibilityState
@@ -69,29 +88,16 @@ namespace Game
Debug, Debug,
}; };
struct DitherData struct RenderingSetup
{ {
static constexpr uint32_t BrightnessBucketCount = 256; bool UseImgui = true;
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;
}; };
class GameRendering class GameRendering
{ {
public: public:
static constexpr uint32_t MaxModels = 64; static constexpr uint32_t MaxModels = 64;
static constexpr uint32_t MaxTextures = 64;
static GameRendering& Get(); static GameRendering& Get();
public: public:
@@ -99,26 +105,26 @@ namespace Game
DitherData DitherTextures; DitherData DitherTextures;
public: public:
RenderingSetup SetupData;
bgfx::UniformHandle DefaultSampler; bgfx::UniformHandle DefaultSampler;
Texture Textures[8]; Texture Textures[MaxTextures];
Material Materials[8]; Material Materials[8];
uint32_t ModelCount = 0; uint32_t ModelCount = 0;
Model Models[MaxModels]; Model Models[MaxModels];
int32_t LastWidth = 0; int32_t LastWidth = 0;
int32_t LastHeight = 0; int32_t LastHeight = 0;
uint32_t ResetFlags = BGFX_RESET_NONE; uint32_t ResetFlags = BGFX_RESET_VSYNC;
uint16_t MainViewID = 10; uint16_t MainViewID = 10;
float LastShaderLoadTime = 0.0f; float LastShaderLoadTime = 0.0f;
int32_t DitherRecursion = 1; int32_t DitherRecursion = 1;
Vec4 DefaultBaseColor;
Vec4 DefaultTileColor;
public: public:
void Setup(); void Setup(const RenderingSetup& setup);
void Update(); void Update();
void HandleEvents(); void HandleEvents();
void RenderDebugUI(); void LoadTextures();
void ReloadShaders();
void Shutdown(); void Shutdown();
Generated::ModelHandle GetModelHandleFromPath(const char* path); Gen::ModelHandle GetModelHandleFromPath(const char* path);
}; };
} // namespace Game } // namespace Game

View File

@@ -0,0 +1,147 @@
$input v_color0
$input v_normal
$input v_uv0
$input v_wpos
#include "../common.sh"
SAMPLER2D(s_texColor, 0);
SAMPLER3D(s_ditherSampler, 1);
SAMPLER2D(s_rampSampler, 2);
uniform vec4 u_time;
uniform vec4 u_dotColor;
uniform vec4 u_texInfo;
uniform vec4 u_baseColor;
float calcBrightness(vec3 lightPos, vec3 vertPos, vec3 normal)
{
vec3 lightDir = normalize(lightPos - vertPos);
float diffuse = max(0.0, dot(lightDir, normal));
return diffuse * clamp(1.0 - distance(lightPos, vertPos) * 0.01, 0.0, 1.0);
}
float calcBrightnessDirectional(vec3 sunDir, vec3 normal)
{
return max(0.0, dot(sunDir, normal));
}
vec3 desaturate(vec3 color)
{
return vec3_splat(dot(color, vec3(0.33, 0.34, 0.33)));
}
float dither(float brightness, vec2 inputUv)
{
float globalScale = 6;
// constants
float xRes = u_texInfo.z;
float dotsPerSide = xRes / 16;
float dotsTotal = dotsPerSide * dotsPerSide;
float invXRes = 1 / xRes;
float zRes = dotsTotal;
float invZRes = 1 / zRes;
vec2 rampLookupUv = vec2((0.5 * invXRes + (1 - invXRes) * brightness), 0.5);
float brightnessCurve = texture2D(s_rampSampler, rampLookupUv).r;
// Magic dot frequency calculation
vec2 dx = dFdx(inputUv);
vec2 dy = dFdy(inputUv);
mat2 mat = mat2(dx, dy);
vec4 vectorized = vec4(dx, dy);
float qq = dot(vectorized, vectorized);
float rr = determinant(mat);
float discriminantSqr = max(0.0, qq*qq - 4.0*rr*rr);
float discriminant = sqrt(discriminantSqr);
vec2 freq = sqrt(vec2(qq + discriminant, qq - discriminant) / 2.0);
// Figuring out how many dots we want
float scaleExp = exp2(globalScale);
float spacing = freq.y * scaleExp;
spacing *= dotsPerSide * (1 / globalScale); // todo: just guessed that globalScale is the right variable here
float brightnessSpacingMultiplier = pow(brightnessCurve * 2 + 0.001, -(1 - 0.5));
spacing *= brightnessSpacingMultiplier;
float spacingLog = log2(spacing);
int patternScaleLevel = int(floor(spacingLog));
float patternFractional = spacingLog - patternScaleLevel;
vec2 uv = inputUv / exp2(patternScaleLevel);
// patternFractional *= patternFractional;
// patternFractional = smoothstep(0, 1, patternFractional);
float subLayer = lerp(0.25 * dotsTotal, dotsTotal, 1 - patternFractional);
subLayer = (subLayer - 0.5) * invZRes;
float pattern = texture3D(s_ditherSampler, vec3(uv, subLayer)).r;
float contrast = 0.5 * scaleExp * brightnessSpacingMultiplier * 0.1;
contrast *= pow(freq.y / freq.x, 1.0);
float baseVal = lerp(0.5, brightness, saturate(1.05 / (1 + contrast)));
float threshold = 1 - brightnessCurve;
return saturate((pattern - threshold) * contrast + baseVal);
}
vec4 rgbToCmyk(vec3 rgb)
{
float k = min(1.0 - rgb.r, min(1.0 - rgb.g, 1.0 - rgb.b));
vec3 cmy = vec3(0.0, 0.0, 0.0);
float invK = 1.0 - k;
if (invK != 0.0)
{
cmy.x = 1.0 - rgb.r - k;
cmy.y = 1.0 - rgb.g - k;
cmy.z = 1.0 - rgb.b - k;
cmy /= invK;
}
return saturate(vec4(cmy, k));
}
vec3 cmykToRgb(vec4 cmyk)
{
float invK = 1.0 - cmyk.w;
float r = 1.0 - min(1.0, cmyk.x * invK + cmyk.w);
float g = 1.0 - min(1.0, cmyk.y * invK + cmyk.w);
float b = 1.0 - min(1.0, cmyk.z * invK + cmyk.w);
return saturate(vec3(r,g,b));
}
vec2 rotateUV(vec2 uv, vec2 angle)
{
return uv;
}
void main()
{
// setup
bool isTextured = u_dotColor.x < 0.02;
float testRadius = 30.0;
float testSpeed = 1.0;
vec3 testOffset = vec3(0.0, 0.0, 50.0);
vec3 lightPos = vec3(sin(u_time.x * testSpeed) * testRadius, 5.0, cos(u_time.x * testSpeed) * testRadius);
vec3 baseColor = u_baseColor.xyz;
vec3 texColor = isTextured ? texture2D(s_texColor, v_uv0).xyz : u_dotColor.xyz;
// lighting
float brightness = lerp(0.2, 0.8, calcBrightnessDirectional(vec3(0.5, 0.3, 1.0), v_normal));
// dither
float r = dither(texColor.r * brightness, v_uv0);
float g = dither(texColor.g * brightness, v_uv0);
float b = dither(texColor.b * brightness, v_uv0);
// vec3 ditheredColor = vec3(r,g,b);
vec4 cmyk = rgbToCmyk(texColor * brightness);
cmyk.x = dither(cmyk.x, rotateUV(v_uv0, float2(0.966, 0.259)));
cmyk.y = dither(cmyk.y, rotateUV(v_uv0, float2(0.259, 0.966)));
cmyk.z = dither(cmyk.z, rotateUV(v_uv0, float2(1.000, 0.000)));
cmyk.w = dither(cmyk.w, rotateUV(v_uv0, float2(0.707, 0.707)));
vec3 ditheredColor = cmykToRgb(cmyk);
// finalize
vec3 finalColor = mix(baseColor, ditheredColor, ditheredColor);
gl_FragColor = vec4(finalColor.rg, finalColor.b * 0.99, 1.0);
// gl_FragColor = vec4(ditheredColor, 1.0);
}

View File

@@ -1,7 +1,7 @@
$input a_position, a_normal, a_color0, a_texcoord0 $input a_position, a_normal, a_color0, a_texcoord0
$output v_color0, v_normal, v_uv0, v_wpos $output v_color0, v_normal, v_uv0, v_wpos
#include "common.sh" #include "../common.sh"
void main() void main()
{ {
@@ -9,5 +9,5 @@ void main()
v_color0 = a_color0; v_color0 = a_color0;
v_uv0 = a_texcoord0; v_uv0 = a_texcoord0;
v_wpos = mul(u_model[0], vec4(a_position, 1.0)).xyz; v_wpos = mul(u_model[0], vec4(a_position, 1.0)).xyz;
v_normal = normalize(mul(u_model[0], a_normal)); v_normal = normalize(mul(u_model[0], vec4(a_normal, 1.0)).xyz);
} }

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