Compare commits

...

49 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
86 changed files with 3289 additions and 1076 deletions

Binary file not shown.

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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,5 @@
$texturecPath = ".\cmake-build\texturec.exe" $texturecPath = "..\tools\texturec.exe"
$textureAssetDir = "..\assets\textures" $textureAssetDir = "..\assets\textures"
$outputBaseDir = ".\textures" $outputBaseDir = ".\textures"

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

@@ -16,7 +16,6 @@ namespace WriteTemplates
constexpr char FileHeaderStart[] = constexpr char FileHeaderStart[] =
R"END(#pragma once R"END(#pragma once
#include <cstdint> #include <cstdint>
#include <cassert>
namespace Gen namespace Gen
{ {
@@ -47,7 +46,7 @@ namespace Gen
constexpr char EnumHeader4[] = constexpr char EnumHeader4[] =
R"END( struct %s R"END( struct %s
{ {
static constexpr uint16_t EnumIdx = %u; static constexpr uint16_t TypeIdx = %u;
static constexpr int32_t EntryCount = %u; static constexpr int32_t EntryCount = %u;
enum Enum : %s enum Enum : %s
{ {
@@ -56,6 +55,9 @@ namespace Gen
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( };
@@ -72,6 +74,25 @@ namespace Gen
}; };
)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";
@@ -125,6 +146,7 @@ namespace Gen
} }
// Failed to resolve hash, the type definition chaned since the file was saved! try to match by name. // 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); int32_t nameMatchIdx = serializer.TypeBuf.FindDefByName(typeName);
if (nameMatchIdx < 0) if (nameMatchIdx < 0)
{ {
@@ -167,7 +189,8 @@ namespace Gen
if (WriteDestinations[j] == UINT64_MAX) if (WriteDestinations[j] == UINT64_MAX)
{ {
// Unknown member name or type changed // Unknown member name or type changed
serializer.Skip(childDef.Size); uint16_t count = bx::max(1, matchedDef.ChildArraySizes[matchedDef.ChildIndices[j]]);
serializer.Skip(childDef.Size * count);
continue; continue;
} }
@@ -175,7 +198,10 @@ namespace Gen
constexpr char LoadFuncBodyTypeUpgradeMember3[] = R"END( if (bx::strCmp(memberName, "%s") == 0) constexpr char LoadFuncBodyTypeUpgradeMember3[] = R"END( if (bx::strCmp(memberName, "%s") == 0)
{ {
auto* fieldPtr = reinterpret_cast<%s*>(objBasePtr + WriteDestinations[j]); auto* fieldPtr = reinterpret_cast<%s*>(objBasePtr + WriteDestinations[j]);
isOk = Load(fieldPtr, %u, serializer) && isOk; 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; continue;
} }
)END"; )END";
@@ -205,6 +231,8 @@ namespace Gen
)END"; )END";
constexpr char MetadataStart1[] = R"END( constexpr char MetadataStart1[] = R"END(
namespace Meta { namespace Meta {
constexpr uint16_t CurrentMetaVersion = 1;
struct StrRef struct StrRef
{ {
uint16_t Offset; uint16_t Offset;
@@ -218,28 +246,18 @@ namespace Gen
char Name[64]{"Dummy"}; char Name[64]{"Dummy"};
uint16_t ChildCount = 0; uint16_t ChildCount = 0;
uint16_t ChildIndices[64]{0}; uint16_t ChildIndices[64]{0};
uint16_t ChildArraySizes[64]{0};
StrRef MemberNameIndices[64]{0}; StrRef MemberNameIndices[64]{0};
}; };
struct EnumDef
{
uint32_t Size = 0;
uint32_t Hash = 0;
};
struct MetadataTable struct MetadataTable
{ {
TypeDef TypeDefinitions[%u] TypeDef TypeDefinitions[%u]
{ {
)END"; )END";
constexpr char MetadataTypeEntry6[] = R"END( TypeDef{sizeof(%s), %u, "%s", %u, {%s}, {%s}}, // TODO: Sizeof is wrong!! we don't use padding!!
)END"; constexpr char MetadataTypeEntry7[] = R"END( TypeDef{sizeof(%s), %u, "%s", %u, {%s}, {%s}, {%s}},
constexpr char MetadataEnumStart1[] = R"END( };
EnumDef EnumDefinitions[%u]
{
)END";
constexpr char MetadataEnumEntry2[] = R"END( EnumDef{sizeof(%s::Enum), %u},
)END"; )END";
constexpr char MetadataEnd1[] = R"END( }; constexpr char MetadataEnd1[] = R"END( };
char MemberNameBuffer[64*64*64]{"%s"}; char MemberNameBuffer[64*64*64]{"%s"};
@@ -300,7 +318,8 @@ 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, int32_t bufSize, Def::TypeRef type, const Def::DefinitionFile& definitions) void CppFileWriter::PrintTypeName(
char* buf, int32_t bufSize, Def::TypeRef type, const Def::DefinitionFile& definitions, PrintFlags flags)
{ {
if (buf == nullptr || bufSize == 0 || !IsValid(type)) if (buf == nullptr || bufSize == 0 || !IsValid(type))
{ {
@@ -310,7 +329,8 @@ void CppFileWriter::PrintTypeName(char* buf, int32_t bufSize, Def::TypeRef type,
if (type.FieldKind == Def::EFieldType::DefinedClass) if (type.FieldKind == Def::EFieldType::DefinedClass)
{ {
auto& t = definitions.Types[type.TypeIdx]; auto& t = definitions.Types[type.TypeIdx];
if ((uint32_t)t.TypeFlags & (uint32_t)Def::ETypeFlags::IsNative) if (((uint32_t)t.TypeFlags & (uint32_t)Def::ETypeFlags::IsNative) &&
((uint32_t)flags & (uint32_t)PrintFlags::PrintNativeTypeName))
{ {
bx::strCopy(buf, bufSize, t.NativeCName); bx::strCopy(buf, bufSize, t.NativeCName);
} }
@@ -322,7 +342,10 @@ void CppFileWriter::PrintTypeName(char* buf, int32_t bufSize, Def::TypeRef type,
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");
}
} }
} }
@@ -331,6 +354,7 @@ 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;
if (!IsValid(e.EnumType)) if (!IsValid(e.EnumType))
{ {
@@ -338,12 +362,24 @@ void CppFileWriter::WriteEnums(const Def::DefinitionFile& definitions)
continue; continue;
} }
char Buf[Def::MaxNameLength]{0}; char enumTypeNameBuf[Def::MaxNameLength]{0};
PrintTypeName(Buf, sizeof(Buf), e.EnumType, definitions); PrintTypeName(enumTypeNameBuf, sizeof(enumTypeNameBuf), e.EnumType, definitions);
Write(WriteTemplates::EnumHeader4, e.Name, enumIdx, e.EntryCount, Buf); 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);
@@ -362,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)
@@ -458,7 +511,10 @@ void CppFileWriter::WriteSaveLoadMethods(const Def::DefinitionFile& definitions)
for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx) for (int32_t fieldIdx = 0; fieldIdx < t.FieldCount; ++fieldIdx)
{ {
const char* fieldName = t.FieldNames[fieldIdx]; const char* fieldName = t.FieldNames[fieldIdx];
PrintTypeName(fieldTypeName, sizeof(fieldTypeName), t.FieldTypes[fieldIdx], definitions); // 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::LoadFuncBodyMemberCheck4, fieldName, fieldTypeName, typeName, fieldName);
} }
WriteCpp(WriteTemplates::LoadFuncBodyTypeUpgradeStart); WriteCpp(WriteTemplates::LoadFuncBodyTypeUpgradeStart);
@@ -483,7 +539,7 @@ namespace
void CppFileWriter::WriteMetadata(const Def::DefinitionFile& definitions) void CppFileWriter::WriteMetadata(const Def::DefinitionFile& definitions)
{ {
uint32_t memberNameBufferIdx = 0; uint32_t memberNameBufferIdx = 0;
Write(WriteTemplates::MetadataStart1, definitions.TypeCount); Write(WriteTemplates::MetadataStart1, definitions.TypeCount + definitions.EnumCount);
for (uint16_t i = 0; i < definitions.TypeCount; ++i) for (uint16_t i = 0; i < definitions.TypeCount; ++i)
{ {
auto& type = definitions.Types[i]; auto& type = definitions.Types[i];
@@ -493,7 +549,9 @@ void CppFileWriter::WriteMetadata(const Def::DefinitionFile& definitions)
{ {
if (j != 0) bx::strCat(fieldStr, sizeof(fieldStr), ", "); if (j != 0) bx::strCat(fieldStr, sizeof(fieldStr), ", ");
char numBuf[8]{0}; char numBuf[8]{0};
bx::snprintf(numBuf, sizeof(numBuf), "%u", type.FieldTypes[j].TypeIdx); 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); bx::strCat(fieldStr, sizeof(fieldStr), numBuf);
} }
@@ -512,13 +570,33 @@ void CppFileWriter::WriteMetadata(const Def::DefinitionFile& definitions)
sizeof(MemberNameBuffer) - memberNameBufferIdx, sizeof(MemberNameBuffer) - memberNameBufferIdx,
type.FieldNames[j]); type.FieldNames[j]);
} }
Write(
WriteTemplates::MetadataTypeEntry6, typeStr, type.Hash, type.Name, type.FieldCount, fieldStr, memberIdxStr); 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);
} }
Write(WriteTemplates::MetadataEnumStart1, definitions.EnumCount); for (uint16_t i = 0; i < definitions.EnumCount; ++i)
for (int32_t i = 0; i < definitions.EnumCount; ++i)
{ {
Write(WriteTemplates::MetadataEnumEntry2, definitions.Enums[i].Name, definitions.Enums[i].Hash); 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); Write(WriteTemplates::MetadataEnd1, MemberNameBuffer);
} }

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,7 +28,12 @@ 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, Def::TypeRef 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);

View File

@@ -283,6 +283,12 @@ Parser::Result Parser::HandleEnum()
e.EnumType = {(uint16_t)Gen::KnownType::i32, Def::EFieldType::DefinedClass}; 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());
while (!CmpAdvance("}", Res)) while (!CmpAdvance("}", Res))

View File

@@ -25,6 +25,12 @@ namespace Def
DynamicArray = 1 << 0, DynamicArray = 1 << 0,
}; };
enum class EEnumFlags : uint32_t
{
None = 0,
FlagsEnum = 1,
};
struct TypeRef struct TypeRef
{ {
uint16_t TypeIdx = UINT16_MAX; uint16_t TypeIdx = UINT16_MAX;
@@ -58,6 +64,7 @@ namespace Def
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{};
@@ -260,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};
@@ -301,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 * 1024;
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

@@ -320,6 +320,14 @@ namespace Gen
return mat; 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) float Magnitude(const Vec4& vec)
{ {
return bx::sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z + vec.w * vec.w); return bx::sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z + vec.w * vec.w);
@@ -385,6 +393,37 @@ namespace Gen
return true; 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) void Translate(Transform& trans, Vec3 offset)
{ {
trans.Position += Vec3{offset.x, offset.y, offset.z}; trans.Position += Vec3{offset.x, offset.y, offset.z};

View File

@@ -75,11 +75,13 @@ namespace Gen
Vec3 EulerFromRotation(const Mat4& rotation); Vec3 EulerFromRotation(const Mat4& rotation);
Mat4 RotationFromEuler(const Vec3& euler); Mat4 RotationFromEuler(const Vec3& euler);
Mat4 RotationFromQuaternion(const Vec4& quaternion);
float DotProduct(Vec3 a, Vec3 b); float DotProduct(Vec3 a, Vec3 b);
Vec3 CrossProduct(Vec3 a, Vec3 b); Vec3 CrossProduct(Vec3 a, Vec3 b);
Vec3 CrossProductFromPlane(Vec3 a, Vec3 b, Vec3 c); Vec3 CrossProductFromPlane(Vec3 a, Vec3 b, Vec3 c);
bool RayPlaneIntersect(Vec3 l1, Vec3 l2, Vec3 p1, Vec3 p2, Vec3 p3, Vec3& out); 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 Translate(Transform& trans, Vec3 offset);
void TranslateLocal(Transform& trans, Vec3 offset); void TranslateLocal(Transform& trans, Vec3 offset);

View File

@@ -37,16 +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

View File

@@ -33,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,6 +2,7 @@
#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
{ {
@@ -9,12 +10,14 @@ namespace Game
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);
} }
@@ -60,6 +63,8 @@ namespace Game
} }
Vec2 GetMousePos() Vec2 GetMousePos()
{ {
// TODO: fix this!!
if (GImGui == nullptr) return {};
ImVec2 pos = ImGui::GetMousePos(); ImVec2 pos = ImGui::GetMousePos();
return {pos.x, pos.y}; return {pos.x, pos.y};
} }

View File

@@ -32,12 +32,6 @@ namespace Game
int64_t StartTime = 0; int64_t StartTime = 0;
}; };
// TODO: move to generated data and save
struct SavedPlayerConfig
{
Gen::SavedEntityRenderData TabletRenderData;
};
struct PlayerData struct PlayerData
{ {
Gen::Transform PlayerCamTransform; Gen::Transform PlayerCamTransform;
@@ -48,24 +42,26 @@ namespace Game
float FreeflyYRot = 0.0f; float FreeflyYRot = 0.0f;
float WalkXRot = 0.0f; float WalkXRot = 0.0f;
float WalkYRot = 0.0f; float WalkYRot = 0.0f;
CameraMode CameraM = CameraMode::Freefly; CameraMode CameraM = CameraMode::Walk;
InputMode InputM = InputMode::Game; InputMode InputM = InputMode::Game;
InteractionMode InteractionM = InteractionMode::Walk; InteractionMode InteractionM = InteractionMode::Walk;
float MouseSensitivity = 1.0f; float MouseSensitivity = 1.0f;
float MovementSpeed = 10.0f; float MovementSpeed = 10.0f;
SavedPlayerConfig Config; 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;
Gen::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 ShortenLogFileNames = true;
bool ShowStats = true; bool ShowStats = true;
@@ -75,7 +71,6 @@ namespace Game
{ {
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"
@@ -26,6 +29,13 @@ namespace Game
{ {
void EntityRenderData::Render(const Model* models, const Material* materials, const Texture* textures) void EntityRenderData::Render(const Model* models, const Material* materials, const Texture* textures)
{ {
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 (models == nullptr || materials == nullptr || textures == nullptr) return;
if (!Gen::IsValid(ModelH) || MaterialHandle >= EMaterial::EntryCount) return; if (!Gen::IsValid(ModelH) || MaterialHandle >= EMaterial::EntryCount) return;
if (!Visible) return; if (!Visible) return;
@@ -93,24 +103,33 @@ namespace Game
bool IsGaming = GetInstance().Player.InputM == InputMode::Game; bool IsGaming = GetInstance().Player.InputM == InputMode::Game;
bool captureMouse = IsGaming && GetInstance().Player.InteractionM == InteractionMode::Walk; bool captureMouse = IsGaming && GetInstance().Player.InteractionM == InteractionMode::Walk;
SDL_SetWindowRelativeMouseMode(GetShared().Window.SDLWindow, captureMouse); SDL_SetWindowRelativeMouseMode(GetShared().Window.SDLWindow, captureMouse);
auto& IO = ImGui::GetIO();
IO.ConfigFlags = FlagBool(IO.ConfigFlags, ImGuiConfigFlags_NoMouse | ImGuiConfigFlags_NoKeyboard, IsGaming); auto& rendering = GameRendering::Get();
GameRendering::Get().UIVisible = IsGaming ? UIVisibilityState::Game : UIVisibilityState::Debug; 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); needReset |= LevelEntities.Setup(storagePtr, needReset);
Puzzle::Setup(); Puzzle::Setup();
UIQuads.Count = 0;
PuzzleTiles.Count = 0;
bx::Error err; bx::Error err;
bx::DirectoryReader dirIter; bx::DirectoryReader dirIter;
@@ -169,8 +188,16 @@ namespace Game
Cubes.Get(PlayerOutsideViewCube).Setup(); Cubes.Get(PlayerOutsideViewCube).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)
@@ -178,17 +205,92 @@ namespace Game
Puzzles[i].Setup(); Puzzles[i].Setup();
} }
} }
TabletHandle = UIQuads.New();
PuzzleUI.Setup();
ReloadLevelEntities();
UpdatePlayerInputMode(); UpdatePlayerInputMode();
for (int32_t i = 0; i < BX_COUNTOF(Puzzles); ++i)
{ {
Deserializer d; Puzzles[i].WorldPosition = {0.0f, 0.0f, i * 50.0f};
d.Init("game/data/static/uiconfig.dat", "UICO");
d.ReadT(GetInstance().Player.Config.TabletRenderData);
d.Finish();
} }
} }
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()
{ {
ZoneScopedN("Level update"); ZoneScopedN("Level update");
@@ -240,9 +342,38 @@ namespace Game
} }
else if (player.CameraM == CameraMode::Walk) else if (player.CameraM == CameraMode::Walk)
{ {
TranslateLocal(player.PlayerCamTransform, {0.0f, 0.0f, inputVec.z}); auto newTransform = player.PlayerCamTransform;
TranslateLocal(player.PlayerCamTransform, {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;
@@ -256,18 +387,6 @@ namespace Game
UpdatePlayerInputMode(); UpdatePlayerInputMode();
} }
// UI Tablet
if (IsValid(TabletHandle))
{
auto& tablet = UIQuads.Get(TabletHandle);
tablet.EData.LoadFromSaved(player.Config.TabletRenderData);
UpdateMatrix(player.PlayerCamTransform);
tablet.EData.Transform.Rotation = player.PlayerCamTransform.Rotation;
Rotate(tablet.EData.Transform, {0.5f * bx::kPi, 0.0f, 0.0f});
tablet.EData.Transform.Position =
player.PlayerCamTransform.Position + AxisForward(player.PlayerCamTransform.M) * 1.0f;
}
// Cubes // Cubes
for (uint16_t i = 0; i < Cubes.Count; ++i) for (uint16_t i = 0; i < Cubes.Count; ++i)
{ {
@@ -275,12 +394,21 @@ 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)
{ {
Puzzles[i].IsActive = GetInstance().DebugData.SelectedDebugLevel == 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);
} }
@@ -316,10 +444,12 @@ namespace Game
Cubes.Render(models, materials, textures); Cubes.Render(models, materials, textures);
Tests.Render(models, materials, textures); Tests.Render(models, materials, textures);
PuzzleTiles.Render(models, materials, textures); PuzzleTiles.Render(models, materials, textures);
PuzzleTileCovers.Render(models, materials, textures);
if (player.InteractionM == InteractionMode::ReadTablet) if (player.InteractionM == InteractionMode::ReadTablet)
{ {
UIQuads.Render(models, materials, textures); UIQuads.Render(models, materials, textures);
} }
LevelEntities.Render(models, materials, textures);
} }
void Cube::Setup() void Cube::Setup()
@@ -343,68 +473,50 @@ namespace Game
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 = EMaterial::Default; tile.EData.MaterialHandle = EMaterial::Default;
UIPlacedCards[i] = level.UIQuads.New(); for (int32_t j = 0; j < Puzzle::Config::MaxCoversInTile; ++j)
auto& quad = level.UIQuads.Get(UIPlacedCards[i]); {
quad.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/plane.glb"); int32_t idx = i * Puzzle::Config::MaxCoversInTile + j;
quad.EData.MaterialHandle = EMaterial::UI; CoverHandles[idx] = level.PuzzleTileCovers.New();
PuzzleTileCover& cover = level.PuzzleTileCovers.Get(CoverHandles[idx]);
cover.EData.Visible = false;
}
} }
for (int32_t i = 0; i < Puzzle::Config::MaxAvailableStacks * WorldPuzzle::UIAvailableCardMaxStackPreview; ++i)
for (int32_t i = 0; i < BX_COUNTOF(EndHandles); ++i)
{ {
UIAvailableCards[i] = level.UIQuads.New(); EndHandles[i] = level.PuzzleTiles.New();
auto& quad = level.UIQuads.Get(UIAvailableCards[i]); PuzzleTileEntity& tile = level.PuzzleTiles.Get(EndHandles[i]);
quad.EData.MaterialHandle = EMaterial::UI; tile.EData.MaterialHandle = EMaterial::Default;
quad.EData.ModelH = GameRendering::Get().GetModelHandleFromPath("models/plane.glb");
} }
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 GetMousePosWorld() Vec3 PuzPosToWorldPos(const PuzzleData& data, int32_t x, int32_t y)
{ {
auto& window = GetShared().Window; return {
Vec2 mousePos = GetMousePos(); (float)x * Puzzle::Config::CardScaleWorld,
mousePos.x = mousePos.x / window.WindowWidth; -5.0f,
mousePos.y = mousePos.y / window.WindowHeight; (float)(data.HeightTiles / 2 - y - 1) * Puzzle::Config::CardScaleWorld,
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 WorldPuzzle::Update() void WorldPuzzle::Update()
@@ -413,91 +525,6 @@ namespace Game
auto& staticCards = Puzzle::GetStaticPuzzleData().Cards; auto& staticCards = Puzzle::GetStaticPuzzleData().Cards;
auto& visuals = Puzzle::GetStaticPuzzleData().Visuals; auto& visuals = Puzzle::GetStaticPuzzleData().Visuals;
Transform& camTransform = GetInstance().Player.PlayerCamTransform;
UpdateMatrix(camTransform);
Vec3 cameraPos = camTransform.Position;
Vec2 uiOffset = Vec2{static_cast<float>(Data.WidthTiles / Puzzle::Config::CardSize - 1),
static_cast<float>(Data.HeightTiles / Puzzle::Config::CardSize) - 1};
uiOffset *= -UICardOffset * 0.5f;
Transform& boardTransform = level.UIQuads.Get(level.TabletHandle).EData.Transform;
Transform tileOriginTransform = boardTransform;
tileOriginTransform.Position += AxisForward(camTransform.M) * -0.01f;
TranslateLocal(tileOriginTransform, Vec3{uiOffset.x, 0.0f, uiOffset.y} * 1.0f);
UpdateMatrix(tileOriginTransform);
Vec3 mousePosWorld = GetMousePosWorld();
// Available Cards
for (int32_t i = 0; i < Puzzle::Config::MaxAvailableStacks; ++i)
{
auto& card = Data.AvailableCards[i];
for (int32_t j = 0; j < UIAvailableCardMaxStackPreview; j++)
{
auto& quad = level.UIQuads.Get(UIAvailableCards[i * UIAvailableCardMaxStackPreview + j]);
if (i < Data.AvailableCardCount & j < card.MaxAvailableCount - card.UsedCount)
{
quad.EData.Visible = IsActive;
quad.EData.TextureHandle = Puzzle::IsValid(Data.AvailableCards[i].RefCard)
? staticCards[Data.AvailableCards[i].RefCard.Idx].BoardTextureHandle
: Gen::TextureHandle{};
quad.EData.Transform.Position = tileOriginTransform.Position;
quad.EData.Transform.Rotation = camTransform.Rotation;
TranslateLocal(quad.EData.Transform,
Vec3{j * 0.05f + i * 1.2f, 6.0f + (j % 2 == 0 ? 0.02f : 0.0f), j * 0.001f} *
UICardOffset * UICardScale);
Rotate(quad.EData.Transform, Vec3{bx::kPi * 0.5f, 0.0f, (1.0f - 0 * 0.5f) * bx::kPi});
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 (j == 0)
{
Vec3 quadPlaneIntersectPos;
if (IsQuadHovered(quad.EData.Transform, mousePosWorld, quadPlaneIntersectPos) &&
DraggedAvailableCardIdx == UINT16_MAX && DraggedCard.X == -1 &&
GetMouseButtonPressedNow(MouseButton::Left))
{
DraggedAvailableCardIdx = i;
}
if (DraggedAvailableCardIdx == i)
{
Vec3 dragPos = quadPlaneIntersectPos;
dragPos -= AxisForward(camTransform.M) * 0.01f;
quad.EData.Transform.Position = dragPos;
Vec3 boardPos = GlobalToLocalPoint(tileOriginTransform, quadPlaneIntersectPos);
Vec3 boardTilePos = boardPos / UICardOffset;
int32_t xPos = (int32_t)bx::round(boardTilePos.x);
int32_t yPos = (int32_t)bx::round(boardTilePos.z);
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;
}
}
}
// Board // 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)
{ {
@@ -506,118 +533,96 @@ namespace Game
int32_t cardIdx = y * Puzzle::Config::MaxPuzzleSizeCards + x; int32_t cardIdx = y * Puzzle::Config::MaxPuzzleSizeCards + x;
Gen::PlacedPuzzleCard& card = Data.PlacedCards[cardIdx]; Gen::PlacedPuzzleCard& card = Data.PlacedCards[cardIdx];
auto& tile = level.PuzzleTiles.Get(TileHandles[cardIdx]); auto& tile = level.PuzzleTiles.Get(TileHandles[cardIdx]);
auto& quad = level.UIQuads.Get(UIPlacedCards[cardIdx]);
bool isValid = Puzzle::IsValid(card.RefCard); bool isValid = Puzzle::IsValid(card.RefCard);
auto& staticCard = isValid ? staticCards[card.RefCard.Idx] : staticCards[0];
// World Tile // World Tile
tile.EData.Visible = IsActive; tile.EData.Visible = true;
tile.EData.ModelH = isValid ? staticCards[card.RefCard.Idx].ModelHandle : staticCards[0].ModelHandle; tile.EData.ModelH = staticCard.BaseModelHandle;
tile.EData.TextureHandle = staticCard.ModelTextureHandle;
tile.EData.DotColor = visuals.TileDotColor; tile.EData.DotColor = visuals.TileDotColor;
tile.EData.BaseColor = visuals.TileBaseColor; tile.EData.BaseColor = visuals.TileBaseColor;
Vec3 cardPos = { Vec3 cardPos = PuzPosToWorldPos(Data, card.Position.X, card.Position.Y);
(float)card.Position.X * Puzzle::Config::CardScaleWorld,
-5.0f,
(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.Position = 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);
// UI Quad // Covers
quad.EData.Visible = isValid && IsActive; if (IsValid(staticCard.BaseModelHandle))
quad.EData.TextureHandle =
isValid ? staticCards[card.RefCard.Idx].BoardTextureHandle : Gen::TextureHandle{};
quad.EData.DotColor = card.IsLocked ? Puzzle::GetStaticPuzzleData().Visuals.DisabledCardTint
: Vec4{1.0f, 1.0f, 1.0f, 1.0f};
quad.EData.Transform.Position = tileOriginTransform.Position;
quad.EData.Transform.Rotation = camTransform.Rotation;
TranslateLocal(quad.EData.Transform,
Vec3{(float)card.Position.X, (float)card.Position.Y, 0.0f} * UICardOffset * UICardScale);
Rotate(quad.EData.Transform, Vec3{bx::kPi * 0.5f, 0.0f, (1.0f - card.Rotation * 0.5f) * bx::kPi});
quad.EData.Transform.Scale = {UICardScale, UICardScale, UICardScale};
Vec3 quadPlaneIntersectPos;
if (isValid && IsQuadHovered(quad.EData.Transform, mousePosWorld, quadPlaneIntersectPos))
{ {
if (!card.IsLocked && DraggedCard.X == -1 && DraggedAvailableCardIdx == UINT16_MAX) auto& model = GameRendering::Get().Models[staticCard.BaseModelHandle.ModelIdx];
for (int32_t i = 0; i < model.SocketCount; ++i)
{ {
if (GetMouseButtonPressedNow(MouseButton::Left)) auto& cover =
{ level.PuzzleTileCovers.Get(CoverHandles[cardIdx * Puzzle::Config::MaxCoversInTile + i]);
DraggedCard.X = x; cover.EData.Visible = IsActive;
DraggedCard.Y = y; cover.EData.ModelH = staticCard.Sockets[i].Model;
} cover.EData.Transform = tile.EData.Transform;
if (GetMouseButtonPressedNow(MouseButton::Right)) cover.EData.MaterialHandle = EMaterial::Default;
{ cover.EData.BaseColor = {0.2f, 0.1f, 0.7f, 1.0f};
Puzzle::RotateCard(card); 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));
}
if (DraggedCard.X == x && DraggedCard.Y == y)
{
Vec3 dragPos = quadPlaneIntersectPos;
dragPos -= AxisForward(camTransform.M) * 0.01f;
quad.EData.Transform.Position = dragPos;
Vec3 boardPos = GlobalToLocalPoint(tileOriginTransform, quadPlaneIntersectPos);
Vec3 boardTilePos = boardPos / UICardOffset;
int32_t xPos = (int32_t)bx::round(boardTilePos.x);
int32_t yPos = (int32_t)bx::round(boardTilePos.z);
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)
{
LOG("CHECK: %u", Data.AvailableCards[availCardIdx].RefCard.Idx);
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;
} }
} }
} }
} }
// 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,159 +1,29 @@
#pragma once #pragma once
#include "../engine/Shared.h" #include "../engine/Shared.h"
#include "Log.h" #include "Entity.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; \
}; \
inline bool IsValid(X h) \
{ \
return h.Idx != UINT16_MAX; \
}
namespace Game namespace Game
{ {
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;
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(UIQuadEntityHandle);
struct UIQuadEntity
{
EntityRenderData EData;
};
ENTITY_HANDLE(LevelEntityHandle);
struct LevelEntity
{
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, const Texture* textures)
{
if (!IsEnabled) return;
for (uint16_t i = 0; i < Count; ++i)
{
Get({i}).EData.Render(models, materials, textures);
}
}
};
struct WorldPuzzle struct WorldPuzzle
{ {
static constexpr Gen::Vec2 WorldCardSize{10.0f, 10.0f}; static constexpr Gen::Vec2 WorldCardSize{10.0f, 10.0f};
static constexpr float UICardScale = 0.05f;
static constexpr float UICardOffset = 2.1f * UICardScale;
static constexpr int32_t UIAvailableCardMaxStackPreview = 3;
Gen::PuzzleData Data; Gen::PuzzleData Data;
Gen::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];
UIQuadEntityHandle UIAvailableCards[Puzzle::Config::MaxAvailableStacks * UIAvailableCardMaxStackPreview]; PuzzleTileEntityHandle EndHandles[Puzzle::Config::MaxPuzzleSizeCards];
Gen::PuzPos DraggedCard{-1, -1}; PuzzleTileEntityHandle WallHandle;
uint16_t DraggedAvailableCardIdx = UINT16_MAX; PuzzleTileEntityHandle DoorHandle;
bool IsSetup = false; bool IsSetup = false;
bool IsActive = false; bool IsActive = false;
bool IsSolved = false;
void Setup(); void Setup();
void Update(); void Update();
@@ -164,20 +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; EntityManager<LevelEntity, LevelEntityHandle, 64> LevelEntities;
CubeHandle PlayerOutsideViewCube; CubeHandle PlayerOutsideViewCube;
UIQuadEntityHandle TabletHandle; LevelEntityHandle BackgroundEntityHandles[16];
public: public:
Gen::StaticPuzzleData PuzzleData; Gen::StaticPuzzleData PuzzleData;
WorldPuzzle Puzzles[3]; 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, const Texture* textures); void Render(uint16_t ViewID, const Model* models, const Material* materials, const Texture* textures);
void ReloadLevelEntities();
}; };
} // namespace Game } // namespace Game

View File

@@ -17,6 +17,7 @@ namespace
{ {
char LineBuffer[LogInternal::MaxLineSize]{0}; char LineBuffer[LogInternal::MaxLineSize]{0};
char OutBuffer[LogInternal::MaxLineSize]{0}; char OutBuffer[LogInternal::MaxLineSize]{0};
char OutBufferUI[LogInternal::MaxLineSize]{0};
bx::HandleHashMapT<1024> OnceMap; bx::HandleHashMapT<1024> OnceMap;
LogHistory History; LogHistory History;
@@ -36,11 +37,13 @@ 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, OutBuffer); bx::strCopy(&History.LogBuffer[History.WriteIdx * LogInternal::MaxLineSize], LogInternal::MaxLineSize, OutBufferUI);
History.WriteTime[History.WriteIdx] = bx::getHPCounter(); History.WriteTime[History.WriteIdx] = bx::getHPCounter();
History.WriteType[History.WriteIdx] = logType;
bx::strCopy(&History.FileBuffer[History.WriteIdx * LogInternal::MaxLineSize], LogInternal::MaxLineSize, file); bx::strCopy(&History.FileBuffer[History.WriteIdx * LogInternal::MaxLineSize], LogInternal::MaxLineSize, file);
History.LineBuffer[History.WriteIdx] = line; History.LineBuffer[History.WriteIdx] = line;

View File

@@ -40,6 +40,7 @@ struct LogHistory
uint32_t LineBuffer[LogInternal::LogHistorySize]{0}; uint32_t LineBuffer[LogInternal::LogHistorySize]{0};
int32_t WriteIdx = 0; int32_t WriteIdx = 0;
int64_t WriteTime[LogInternal::LogHistorySize]{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, ...);

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;
} }

View File

@@ -1,7 +1,6 @@
#include "../gen/Def.h" #include "../gen/Def.h"
#include "Gen.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"
@@ -22,6 +21,7 @@ namespace
StaticPuzzleData StaticData; StaticPuzzleData StaticData;
StaticPuzzleCard InvalidCard; StaticPuzzleCard InvalidCard;
PlacedPuzzleCard InvalidPlacedCard;
} // namespace } // namespace
namespace Puzzle namespace Puzzle
@@ -104,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);
@@ -240,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)
@@ -288,12 +306,32 @@ namespace Puzzle
bx::snprintf(buf, bufSize, "%s/%u.pzl", Puzzle::PuzzleFileDir, puzID); bx::snprintf(buf, bufSize, "%s/%u.pzl", Puzzle::PuzzleFileDir, puzID);
} }
PlacedPuzzleCard& GetCardAt(PuzzleData& obj, PuzPos pos)
{
if (pos.X < 0 || pos.X >= Config::MaxPuzzleSizeCards || pos.Y < 0 || pos.Y >= Config::MaxPuzzleSizeCards)
{
LOG_ERROR("Invalid card access at %i %i!!", pos.X, pos.Y);
return InvalidPlacedCard;
}
return obj.PlacedCards[pos.Y * Config::MaxPuzzleSizeCards + pos.X];
}
PlacedPuzzleCard& GetInitialCardAt(PuzzleData& obj, PuzPos pos)
{
if (pos.X < 0 || pos.X >= Config::MaxPuzzleSizeCards || pos.Y < 0 || pos.Y >= Config::MaxPuzzleSizeCards)
{
LOG_ERROR("Invalid card access at %i %i!!", pos.X, pos.Y);
return InvalidPlacedCard;
}
return obj.InitialPlacedCards[pos.Y * Config::MaxPuzzleSizeCards + pos.X];
}
bool ReturnPlacedCard(PuzzleData& obj, PuzPos targetPos) 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) if (GetFlag(placedCard.Flags, PlacedPuzzleCardFlags::Locked))
{ {
LOG_WARN("Card at %i %i is locked!", targetPos.X, targetPos.Y); LOG_WARN("Card at %i %i is locked!", targetPos.X, targetPos.Y);
return false; return false;
@@ -330,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))
{ {
@@ -356,17 +430,34 @@ namespace Puzzle
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;
@@ -375,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();
@@ -386,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();
@@ -401,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;
@@ -414,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,
@@ -458,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();
} }
@@ -534,7 +611,6 @@ namespace Puzzle
obj.AvailableCardCount++; obj.AvailableCardCount++;
} }
} }
dataChanged = true;
} }
ImGui::EndDragDropTarget(); ImGui::EndDragDropTarget();
} }
@@ -547,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);
@@ -563,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);
@@ -582,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

@@ -12,16 +12,20 @@ namespace Puzzle
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,15 +36,19 @@ 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); void RotateCard(PlacedPuzzleCard& card);
PlacedPuzzleCard& GetCardAt(PuzzleData& obj, PuzPos pos);
PlacedPuzzleCard& GetInitialCardAt(PuzzleData& obj, PuzPos pos);
// TODO: targetPos is of type CardPos // TODO: targetPos is of type CardPos
bool ReturnPlacedCard(PuzzleData& obj, PuzPos targetPos); bool ReturnPlacedCard(PuzzleData& obj, PuzPos targetPos);
// TODO: targetPos is of type CardPos // 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);
void ResetPuzzle(PuzzleData& puz);
struct PuzzleSolver struct PuzzleSolver
{ {

View File

@@ -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;
} }

View File

@@ -2,12 +2,16 @@
#include "Gen.h" #include "Gen.h"
#include "Global.h" #include "Global.h"
#include "Instance.h" #include "Instance.h"
#include "Log.h"
#include "Mesh.h" #include "Mesh.h"
#include "Puzzle.h" #include "Puzzle.h"
#include "Tools.h" #include "Tools.h"
#include "bgfx/bgfx.h"
#include "bx/filepath.h" #include "bx/filepath.h"
#include "bx/string.h"
#include "bx/timer.h" #include "bx/timer.h"
#include "rendering/Rendering.h"
#include <imgui.h> #include <imgui.h>
#include <tracy/Tracy.hpp> #include <tracy/Tracy.hpp>
@@ -37,6 +41,7 @@ namespace Tools
bool EntityDataSettings(Gen::SavedEntityRenderData& data) bool EntityDataSettings(Gen::SavedEntityRenderData& data)
{ {
ImGui::PushID(&data);
bool changed = false; bool changed = false;
changed |= ModelDropdown(data.Model); changed |= ModelDropdown(data.Model);
changed |= MaterialDropdown(data.Material); changed |= MaterialDropdown(data.Material);
@@ -45,15 +50,16 @@ namespace Tools
changed |= ImGui::Checkbox("Visible", &data.Visible); changed |= ImGui::Checkbox("Visible", &data.Visible);
changed |= ImGui::ColorEdit4("Color 1", &data.BaseColor.x); changed |= ImGui::ColorEdit4("Color 1", &data.BaseColor.x);
changed |= ImGui::ColorEdit4("Color 2", &data.HighlightColor.x); changed |= ImGui::ColorEdit4("Color 2", &data.HighlightColor.x);
ImGui::PopID();
return changed; return changed;
} }
bool ModelDropdown(Gen::ModelHandle& modelHandle) bool ModelDropdown(Gen::ModelHandle& modelHandle, const char* title)
{ {
bool changed = false; 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("Model", name)) if (ImGui::BeginCombo(title, assetName))
{ {
for (int32_t i = 0; i < R.ModelCount; ++i) for (int32_t i = 0; i < R.ModelCount; ++i)
{ {
@@ -68,12 +74,12 @@ namespace Tools
return changed; return changed;
} }
bool TextureDropdown(Gen::TextureHandle& texHandle) bool TextureDropdown(Gen::TextureHandle& texHandle, const char* title)
{ {
bool changed = false; bool changed = false;
auto& R = Game::GameRendering::Get(); auto& R = Game::GameRendering::Get();
const char* name = GetAssetPath(texHandle.Asset); const char* name = GetAssetPath(texHandle.Asset);
if (ImGui::BeginCombo("Texture", name)) if (ImGui::BeginCombo(title, name))
{ {
for (int32_t i = 0; i < R.MaxTextures; ++i) for (int32_t i = 0; i < R.MaxTextures; ++i)
{ {
@@ -120,6 +126,50 @@ namespace Tools
return changed; 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 TransformUI(Gen::Transform& transform)
{ {
bool changed = false; bool changed = false;
@@ -134,7 +184,74 @@ namespace Tools
return changed; return changed;
} }
void RenderDebugUI(Game::GameRendering& rendering) 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& time = Game::GetInstance().Time;
auto& shared = Game::GetShared(); auto& shared = Game::GetShared();
@@ -142,107 +259,84 @@ namespace Tools
auto& level = Game::GetInstance().GameLevel; auto& level = Game::GetInstance().GameLevel;
auto& player = Game::GetInstance().Player; auto& player = Game::GetInstance().Player;
if (rendering.UIVisible == Game::UIVisibilityState::Debug) if (ImGui::Begin("Rendering"))
{ {
ZoneScopedN("DebugUI"); if (rendering.LastShaderLoadTime >= 0.0f)
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
{ {
debug.DebugCardRotation++; ImGui::TextColored({0.2f, 0.9f, 0.2f, 1.0f},
if (debug.DebugCardRotation >= 4) debug.DebugCardRotation = 0; "Shader loaded %.0f seconds ago",
time.Now - rendering.LastShaderLoadTime);
} }
if (ImGui::Begin("Log")) else
{ {
ImGui::Checkbox("Shorten File Names", &debug.ShortenLogFileNames); ImGui::TextColored({0.9f, 0.2f, 0.2f, 1.0f}, "Shader load Failiure!");
ImGui::BeginTable(
"tbl", 4, ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_SizingFixedFit);
ImGui::TableSetupColumn("Time", ImGuiTableColumnFlags_NoResize);
ImGui::TableSetupColumn("Log");
ImGui::TableSetupColumn("Line", ImGuiTableColumnFlags_NoResize);
ImGui::TableSetupColumn("File", ImGuiTableColumnFlags_NoResize);
ImGui::TableHeadersRow();
for (int32_t i = 0; i < bx::min(100, LogInternal::LogHistorySize); ++i)
{
int32_t idx = GetLogHistory().WriteIdx - i - 1;
if (idx < 0) idx += LogInternal::LogHistorySize;
const char* line = &GetLogHistory().LogBuffer[idx * LogInternal::MaxLineSize];
if (line[0] != 0)
{
int64_t timeOffset = GetLogHistory().WriteTime[idx] - time.StartTime;
double writeTime = (double)timeOffset / bx::getHPFrequency();
uint32_t fileLine = GetLogHistory().LineBuffer[idx];
const char* filePath = &GetLogHistory().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);
}
}
ImGui::EndTable();
} }
ImGui::End(); ImGui::SameLine();
if (ImGui::Begin("Rendering")) if (ImGui::Button("Reload Level"))
{ {
if (rendering.LastShaderLoadTime >= 0.0f) level = {};
{ level.Setup(shared.Game);
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!");
}
if (ImGui::Button("Reload Meshes"))
{
LoadModels(rendering.Models, rendering.ModelCount);
}
ImGui::SameLine();
if (ImGui::Button("Reload Level"))
{
level = {};
level.Setup(shared.Game);
}
ImGui::SliderFloat("Mouse Sensitivity", &player.MouseSensitivity, 0.1f, 5.0f); ImGui::DragFloat3("Player Pos", &player.PlayerCamTransform.Position.x);
ImGui::SliderFloat("Player Speed", &player.MovementSpeed, 1.0f, 30.0f); 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 ImGui Demo", &debug.ShowImguiDemo);
ImGui::Checkbox("Show Stats", &debug.ShowStats); ImGui::Checkbox("Show Stats", &debug.ShowStats);
if (debug.ShowImguiDemo) ImGui::ShowDemoWindow(&debug.ShowImguiDemo); if (debug.ShowImguiDemo) ImGui::ShowDemoWindow(&debug.ShowImguiDemo);
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Entity Groups"); if (ImGui::TreeNodeEx("Entity Groups"))
{
ImGui::Checkbox("Cubes", &level.Cubes.IsEnabled); ImGui::Checkbox("Cubes", &level.Cubes.IsEnabled);
ImGui::Checkbox("Tests", &level.Tests.IsEnabled); ImGui::Checkbox("Tests", &level.Tests.IsEnabled);
ImGui::Checkbox("PuzzleTiles", &level.PuzzleTiles.IsEnabled); ImGui::Checkbox("PuzzleTiles", &level.PuzzleTiles.IsEnabled);
ImGui::Checkbox("UIQuads", &level.UIQuads.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::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();
}
ImGui::Text("Game Tablet"); if (ImGui::TreeNodeEx("Background Level"))
if (Tools::EntityDataSettings(player.Config.TabletRenderData)) {
for (int32_t i = 0; i < BX_COUNTOF(player.Config.BackgroundLevelRenderData); ++i)
{ {
Serializer s; if (i > 0) ImGui::Separator();
s.Init("game/data/static/uiconfig.dat", "UICO"); uiconfigChanged |= Tools::EntityDataSettings(player.Config.BackgroundLevelRenderData[i]);
s.WriteT(player.Config.TabletRenderData);
s.Finish();
} }
ImGui::TreePop();
}
ImGui::Separator(); 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")) if (ImGui::Button("Dithergen"))
{ {
DitherGen(rendering.DitherTextures, rendering.DitherRecursion); DitherGen(rendering.DitherTextures, rendering.DitherRecursion);
@@ -269,137 +363,247 @@ namespace Tools
ImGui::Image(rendering.DitherTextures.RampTex.idx, ImGui::Image(rendering.DitherTextures.RampTex.idx,
{BX_COUNTOF(rendering.DitherTextures.BrightnessRamp), 8}); {BX_COUNTOF(rendering.DitherTextures.BrightnessRamp), 8});
} }
Vec3 quadPos = level.UIQuads.Get({0}).EData.Transform.Position; ImGui::TreePop();
ImGui::Text("%f %f %f", quadPos.x, quadPos.y, quadPos.z); }
ImGui::Text("Shader log:"); if (ImGui::TreeNodeEx("Shader log"))
{
ImGui::TextWrapped("%s", Game::GetShared().Dev.ShaderLog); ImGui::TextWrapped("%s", Game::GetShared().Dev.ShaderLog);
ImGui::TreePop();
} }
ImGui::End(); }
if (ImGui::Begin("Textures")) ImGui::End();
{ }
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();
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; void RenderTexturesUI(Game::GameRendering& rendering)
ImGui::PushID("selectable"); {
if (ImGui::Selectable(puzzleData.PuzzleName, isSelected)) if (ImGui::Begin("Textures"))
{ {
debug.SelectedDebugLevel = isSelected ? UINT16_MAX : i; if (ImGui::Button("Reload"))
}
ImGui::PopID();
}
}
ImGui::End();
if (debug.SelectedDebugLevel < BX_COUNTOF(level.Puzzles))
{ {
if (!Puzzle::RenderDebugUI(level.Puzzles[debug.SelectedDebugLevel].Data)) rendering.LoadTextures();
{
debug.SelectedDebugLevel = UINT16_MAX;
}
} }
if (ImGui::Begin("Cards")) for (int32_t i = 0; i < rendering.MaxTextures; ++i)
{ {
Gen::StaticPuzzleData& staticData = Puzzle::GetStaticPuzzleData(); 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();
}
if (ImGui::Button("Save")) 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))
{ {
Puzzle::SaveStaticPuzzleData(); ImGui::Text("%s", mdl.Name);
ImGui::Image(mdl.HeightMapTexture.idx,
ImVec2{(float)Game::HeightMap::Height, (float)Game::HeightMap::Width});
} }
ImGui::SameLine(); else
if (ImGui::Button("Reload"))
{ {
Puzzle::LoadStaticPuzzleData(); 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(); ImGui::Separator();
ImGui::ColorEdit3("Disabled Tint", &staticData.Visuals.DisabledCardTint.x); Gen::StaticPuzzleCard& card = staticData.Cards[i];
ImGui::ColorEdit3("Tile Base Color", &staticData.Visuals.TileBaseColor.x); ImGui::PushID(i);
ImGui::ColorEdit3("Tile Dot Color", &staticData.Visuals.TileDotColor.x); char cardName[64]{0};
bx::snprintf(cardName, sizeof(cardName), "%i", i);
for (int32_t i = 0; i < BX_COUNTOF(staticData.Cards); ++i) ImGui::Selectable(cardName);
if (ImGui::BeginDragDropSource())
{ {
ImGui::Separator(); Puzzle::DrawCard(card, debug.DebugCardRotation, ImGui::GetCursorScreenPos());
ImGui::SetDragDropPayload("cardtype", &i, sizeof(i));
ImGui::EndDragDropSource();
}
Gen::StaticPuzzleCard& card = staticData.Cards[i]; Tools::ModelDropdown(card.BaseModelHandle);
ImGui::PushID(i); Tools::TextureDropdown(card.ModelTextureHandle, "World Texture");
char cardName[64]{0}; Tools::TextureDropdown(card.BoardTextureHandle, "UI Texture");
bx::snprintf(cardName, sizeof(cardName), "%i", i); if (IsValid(card.BaseModelHandle))
ImGui::Selectable(cardName); {
if (ImGui::BeginDragDropSource()) auto& mdl = rendering.Models[card.BaseModelHandle.ModelIdx];
if (mdl.SocketCount > 0 && ImGui::TreeNodeEx("Slots"))
{ {
Puzzle::DrawCard(card, debug.DebugCardRotation, ImGui::GetCursorScreenPos()); for (int32_t sIdx = 0; sIdx < mdl.SocketCount; ++sIdx)
ImGui::SetDragDropPayload("cardtype", &i, sizeof(i));
ImGui::EndDragDropSource();
}
Tools::ModelDropdown(card.ModelHandle);
Tools::TextureDropdown(card.BoardTextureHandle);
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(); Tools::ModelDropdown(card.Sockets[sIdx].Model, mdl.Sockets[sIdx].Name);
ImGui::PushID(x); int val = card.Sockets[sIdx].ConnectionDirection;
auto& node = Puzzle::EditCardNodeAt(card, 0, x, y); ImGui::PushID(sIdx);
if (ImGui::Button(Gen::PuzzleElementType::ShortName[node], {26, 24})) if (ImGui::Combo("Connection Direction", &val, "North\0East\0South\0West\0"))
{ {
int32_t newVal = int32_t(node) + 1; card.Sockets[sIdx].ConnectionDirection = val;
if (newVal >= Gen::PuzzleElementType::EntryCount)
{
newVal = 0;
}
node = Gen::PuzzleElementType::Enum(newVal);
} }
ImGui::PopID(); 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::PopID();
} }
ImGui::PopID();
} }
ImGui::End();
} }
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) if (debug.ShowStats)
{ {
ImGui::SetNextWindowPos({0, 0}); ImGui::SetNextWindowPos({0, 0});
@@ -418,7 +622,7 @@ namespace Tools
ImGui::Text("FPS: %.0f", 1.0 / time.Delta); ImGui::Text("FPS: %.0f", 1.0 / time.Delta);
constexpr ImVec2 FpsPlotSize{200, 60}; constexpr ImVec2 FpsPlotSize{200, 60};
if (ImGui::BeginChild("FpsPlot", FpsPlotSize)) if (ImGui::BeginChild("FpsPlot", FpsPlotSize, 0, ImGuiWindowFlags_NoInputs))
{ {
auto& drawList = *ImGui::GetWindowDrawList(); auto& drawList = *ImGui::GetWindowDrawList();
ImVec2 pos = ImGui::GetWindowPos(); ImVec2 pos = ImGui::GetWindowPos();
@@ -448,6 +652,105 @@ namespace Tools
ImGui::End(); 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() void MeasureFrameEnd()
{ {
FrameTimes[FrameTimeIdx] = bx::getHPCounter(); FrameTimes[FrameTimeIdx] = bx::getHPCounter();

View File

@@ -5,8 +5,8 @@
namespace Tools namespace Tools
{ {
bool EntityDataSettings(Gen::SavedEntityRenderData& data); bool EntityDataSettings(Gen::SavedEntityRenderData& data);
bool ModelDropdown(Gen::ModelHandle& modelHandle); bool ModelDropdown(Gen::ModelHandle& modelHandle, const char* title = "Model");
bool TextureDropdown(Gen::TextureHandle& texHandle); bool TextureDropdown(Gen::TextureHandle& texHandle, const char* title = "Texture");
bool MaterialDropdown(Gen::EMaterial::Enum& material); bool MaterialDropdown(Gen::EMaterial::Enum& material);
bool TransformUI(Gen::Transform& transform); bool TransformUI(Gen::Transform& transform);
void RenderDebugUI(Game::GameRendering& rendering); void RenderDebugUI(Game::GameRendering& rendering);

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.

View File

@@ -82,10 +82,22 @@ 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 TextureHandle BoardTextureHandle
} }
@@ -115,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
@@ -132,6 +150,7 @@ 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)
@@ -153,3 +172,13 @@ type SavedEntityRenderData
ModelHandle Model ModelHandle Model
b Visible b Visible
} }
type SavedPlayerConfig
{
SavedEntityRenderData TabletBackgroundRenderData
SavedEntityRenderData TabletStatusRenderData
TextureHandle TabletStatusNotSolvedTexture
TextureHandle TabletStatusSolvedTexture
SavedEntityRenderData TabletResetRenderData
SavedEntityRenderData BackgroundLevelRenderData Arr(16)
}

View File

@@ -16,11 +16,12 @@ void DitherGen(DitherData& data, int32_t recursion)
for (int32_t i = 0; i < data.BrightnessBucketCount; ++i) for (int32_t i = 0; i < data.BrightnessBucketCount; ++i)
data.BrightnessBuckets[i] = 0; data.BrightnessBuckets[i] = 0;
for (int32_t recursionLevel = 0; recursionLevel < recursion; ++recursionLevel) // add "subdivided" beyer matrix layers
for (int32_t recursionLevel = 0; recursionLevel < recursion - 1; ++recursionLevel)
{ {
int32_t startCount = data.PointCount; int32_t startCount = data.PointCount;
float offset = bx::pow(0.5f, recursionLevel + 1); float offset = bx::pow(0.5f, recursionLevel + 1);
for (int32_t i = 0; i < 4; ++i) for (int32_t i = 1; i < 4; ++i)
{ {
for (int32_t j = 0; j < startCount; ++j) for (int32_t j = 0; j < startCount; ++j)
{ {
@@ -30,6 +31,7 @@ void DitherGen(DitherData& data, int32_t recursion)
} }
} }
// Texture info setup
uint64_t dotsPerSide = bx::round(bx::pow(2, recursion)); uint64_t dotsPerSide = bx::round(bx::pow(2, recursion));
data.DitherTexDepth = dotsPerSide * dotsPerSide; data.DitherTexDepth = dotsPerSide * dotsPerSide;
data.DitherTexWH = 16 * dotsPerSide; data.DitherTexWH = 16 * dotsPerSide;
@@ -41,6 +43,7 @@ void DitherGen(DitherData& data, int32_t recursion)
return; return;
} }
// What does this do?
float invRes = 1.0f / data.DitherTexWH; float invRes = 1.0f / data.DitherTexWH;
for (int32_t z = 0; z < data.DitherTexDepth; ++z) for (int32_t z = 0; z < data.DitherTexDepth; ++z)
{ {
@@ -48,7 +51,7 @@ void DitherGen(DitherData& data, int32_t recursion)
float dotArea = 0.5f / dotCount; float dotArea = 0.5f / dotCount;
float dotRadius = bx::sqrt(dotArea / bx::kPi); float dotRadius = bx::sqrt(dotArea / bx::kPi);
int zOffset = z * data.DitherTexWH * data.DitherTexWH; int32_t zOffset = z * data.DitherTexWH * data.DitherTexWH;
for (int32_t y = 0; y < data.DitherTexWH; ++y) for (int32_t y = 0; y < data.DitherTexWH; ++y)
{ {
int32_t yOffset = y * data.DitherTexWH; int32_t yOffset = y * data.DitherTexWH;
@@ -59,7 +62,9 @@ void DitherGen(DitherData& data, int32_t recursion)
for (int32_t i = 0; i < dotCount; ++i) for (int32_t i = 0; i < dotCount; ++i)
{ {
Vec2 vec = point - data.Points[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 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); float curDist = Magnitude(wrappedVec);
dist = bx::min(dist, curDist); dist = bx::min(dist, curDist);
} }
@@ -67,7 +72,6 @@ void DitherGen(DitherData& data, int32_t recursion)
dist = dist / (dotRadius * 2.4f); dist = dist / (dotRadius * 2.4f);
float val = bx::clamp(1.0f - dist, 0.0f, 1.0f); 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{val, val, val, 1.0f};
// data.DitherTex[x + yOffset + zOffset] = Vec4{1.0, 0.0f, 0.0f, 1.0f};
int32_t bucket = bx::clamp( int32_t bucket = bx::clamp(
uint32_t(val * DitherData::BrightnessBucketCount), 0, DitherData::BrightnessBucketCount - 1); uint32_t(val * DitherData::BrightnessBucketCount), 0, DitherData::BrightnessBucketCount - 1);
data.BrightnessBuckets[bucket] += 1; data.BrightnessBuckets[bucket] += 1;
@@ -84,21 +88,7 @@ void DitherGen(DitherData& data, int32_t recursion)
} }
// Upload textures // Upload textures
if (isValid(data.PreviewTex)) CleanupDitherData(data);
{
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* memPreview = bgfx::makeRef(data.DitherTex, texPixelCount * sizeof(Vec4));
const bgfx::Memory* memFinal = 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)); const bgfx::Memory* memRamp = bgfx::makeRef(data.BrightnessRamp, sizeof(data.BrightnessRamp));
@@ -123,3 +113,22 @@ void DitherGen(DitherData& data, int32_t recursion)
data.RampSampler = bgfx::createUniform("s_rampSampler", bgfx::UniformType::Sampler); 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

@@ -24,3 +24,4 @@ struct DitherData
}; };
void DitherGen(DitherData& data, int32_t recursion); void DitherGen(DitherData& data, int32_t recursion);
void CleanupDitherData(DitherData& data);

View File

@@ -6,6 +6,7 @@
#include "../Tools.h" #include "../Tools.h"
#include "Rendering.h" #include "Rendering.h"
#include "Dither.h"
#include "SDL3/SDL_events.h" // IWYU pragma: keep #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"
@@ -32,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;
} }
@@ -48,11 +139,12 @@ 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) if (rawMem == nullptr)
{ {
LOG_ERROR("Failed to load file, exceeded scratch memory! %s", path); 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);
@@ -127,7 +219,7 @@ 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 || data->data == nullptr || data->size == 0) 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());
@@ -144,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)
{ {
@@ -211,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;
@@ -253,23 +360,26 @@ namespace Game
LoadModels(Models, ModelCount); LoadModels(Models, ModelCount);
ReloadShaders(); ReloadShaders();
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)
@@ -277,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);
} }
@@ -295,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;
@@ -404,6 +520,7 @@ namespace Game
HandleEvents(); HandleEvents();
// Start Rendering // Start Rendering
if (SetupData.UseImgui)
{ {
ZoneScopedN("Imgui Start Frame"); ZoneScopedN("Imgui Start Frame");
imguiBeginFrame(20); imguiBeginFrame(20);
@@ -417,12 +534,13 @@ namespace Game
GetInstance().GameLevel.Render(MainViewID, Models, Materials, Textures); 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();
@@ -441,13 +559,34 @@ 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;
} }

View File

@@ -32,12 +32,36 @@ namespace Game
Gen::TextureHandle TexHandle; 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
{ {
static constexpr uint16_t MaxSocketCount = 16;
bgfx::VertexBufferHandle VertexBuffer = {bgfx::kInvalidHandle}; bgfx::VertexBufferHandle VertexBuffer = {bgfx::kInvalidHandle};
bgfx::IndexBufferHandle IndexBuffer = {bgfx::kInvalidHandle}; bgfx::IndexBufferHandle IndexBuffer = {bgfx::kInvalidHandle};
bgfx::VertexLayout VertLayout; bgfx::VertexLayout VertLayout;
Gen::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
@@ -64,6 +88,11 @@ namespace Game
Debug, Debug,
}; };
struct RenderingSetup
{
bool UseImgui = true;
};
class GameRendering class GameRendering
{ {
public: public:
@@ -76,6 +105,7 @@ namespace Game
DitherData DitherTextures; DitherData DitherTextures;
public: public:
RenderingSetup SetupData;
bgfx::UniformHandle DefaultSampler; bgfx::UniformHandle DefaultSampler;
Texture Textures[MaxTextures]; Texture Textures[MaxTextures];
Material Materials[8]; Material Materials[8];
@@ -89,7 +119,7 @@ namespace Game
int32_t DitherRecursion = 1; int32_t DitherRecursion = 1;
public: public:
void Setup(); void Setup(const RenderingSetup& setup);
void Update(); void Update();
void HandleEvents(); void HandleEvents();
void LoadTextures(); void LoadTextures();

View File

@@ -32,7 +32,7 @@ vec3 desaturate(vec3 color)
float dither(float brightness, vec2 inputUv) float dither(float brightness, vec2 inputUv)
{ {
float globalScale = 8; float globalScale = 6;
// constants // constants
float xRes = u_texInfo.z; float xRes = u_texInfo.z;
@@ -42,31 +42,30 @@ float dither(float brightness, vec2 inputUv)
float zRes = dotsTotal; float zRes = dotsTotal;
float invZRes = 1 / zRes; float invZRes = 1 / zRes;
float2 rampLookupUv = vec2((0.5 * invXRes + (1 - invXRes) * brightness), 0.5); vec2 rampLookupUv = vec2((0.5 * invXRes + (1 - invXRes) * brightness), 0.5);
float brightnessCurve = texture2D(s_rampSampler, rampLookupUv).r; float brightnessCurve = texture2D(s_rampSampler, rampLookupUv).r;
// Magic dot frequency calculation // Magic dot frequency calculation
float2 dx = ddx(inputUv); vec2 dx = dFdx(inputUv);
float2 dy = ddy(inputUv); vec2 dy = dFdy(inputUv);
float2x2 mat = float2x2(dx, dy); mat2 mat = mat2(dx, dy);
float4 vectorized = float4(dx, dy); vec4 vectorized = vec4(dx, dy);
float qq = dot(vectorized, vectorized); float qq = dot(vectorized, vectorized);
float rr = determinant(mat); float rr = determinant(mat);
float discriminantSqr = max(0.0, qq*qq - 4.0*rr*rr); float discriminantSqr = max(0.0, qq*qq - 4.0*rr*rr);
float discriminant = sqrt(discriminantSqr); float discriminant = sqrt(discriminantSqr);
float2 freq = sqrt(float2(qq + discriminant, qq - discriminant) / 2.0); vec2 freq = sqrt(vec2(qq + discriminant, qq - discriminant) / 2.0);
// Figuring out how many dots we want // Figuring out how many dots we want
float scaleExp = exp2(globalScale); float scaleExp = exp2(globalScale);
float spacing = freq.y * scaleExp; float spacing = freq.y * scaleExp;
spacing *= dotsPerSide * 0.125; spacing *= dotsPerSide * (1 / globalScale); // todo: just guessed that globalScale is the right variable here
float brightnessSpacingMultiplier = pow(brightnessCurve * 2 + 0.001, -(1 - 0.5)); float brightnessSpacingMultiplier = pow(brightnessCurve * 2 + 0.001, -(1 - 0.5));
// float brightnessSpacingMultiplier = 1;
spacing *= brightnessSpacingMultiplier; spacing *= brightnessSpacingMultiplier;
float spacingLog = log2(spacing); float spacingLog = log2(spacing);
int patternScaleLevel = floor(spacingLog); int patternScaleLevel = int(floor(spacingLog));
float patternFractional = spacingLog - patternScaleLevel; float patternFractional = spacingLog - patternScaleLevel;
vec2 uv = inputUv / exp2(patternScaleLevel); vec2 uv = inputUv / exp2(patternScaleLevel);
@@ -77,35 +76,72 @@ float dither(float brightness, vec2 inputUv)
float pattern = texture3D(s_ditherSampler, vec3(uv, subLayer)).r; float pattern = texture3D(s_ditherSampler, vec3(uv, subLayer)).r;
float contrast = 2 * scaleExp * brightnessSpacingMultiplier * 0.1; float contrast = 0.5 * scaleExp * brightnessSpacingMultiplier * 0.1;
contrast *= pow(freq.y / freq.x, 1.0); contrast *= pow(freq.y / freq.x, 1.0);
float baseVal = lerp(0.5, brightness, saturate(1.05 / (1 + contrast))); float baseVal = lerp(0.5, brightness, saturate(1.05 / (1 + contrast)));
float threshold = 1 - brightnessCurve + 0.6; float threshold = 1 - brightnessCurve;
return saturate((pattern - threshold) * contrast + baseVal); 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() void main()
{ {
// setup // setup
bool isTextured = u_dotColor.x < 0.02;
float testRadius = 30.0; float testRadius = 30.0;
float testSpeed = 1.0; float testSpeed = 1.0;
vec3 testOffset = vec3(0.0, 0.0, 50.0); vec3 testOffset = vec3(0.0, 0.0, 50.0);
float3 lightPos = vec3(sin(u_time.x * testSpeed) * testRadius, 5.0, cos(u_time.x * testSpeed) * testRadius); vec3 lightPos = vec3(sin(u_time.x * testSpeed) * testRadius, 5.0, cos(u_time.x * testSpeed) * testRadius);
vec3 texColor = u_dotColor.x > 0.1 ? u_dotColor.xyz : texture2D(s_texColor, v_uv0).xyz; vec3 baseColor = u_baseColor.xyz;
vec3 texColor = isTextured ? texture2D(s_texColor, v_uv0).xyz : u_dotColor.xyz;
// lighting // lighting
// float brightness = calcBrightness(lightPos, v_wpos, v_normal); float brightness = lerp(0.2, 0.8, calcBrightnessDirectional(vec3(0.5, 0.3, 1.0), v_normal));
float brightness = lerp(0.5, 0.9, calcBrightnessDirectional(vec3(0.5, 0.3, 1.0), v_normal));
// float brightness = 0.5;
// brightness = lerp(0.2, 1.0, sin(u_time.x) * 0.5 + 0.5);
float r = dither(brightness * texColor.r, v_uv0); // dither
float g = dither(brightness * texColor.g, v_uv0); float r = dither(texColor.r * brightness, v_uv0);
float b = dither(brightness * texColor.b, v_uv0); float g = dither(texColor.g * brightness, v_uv0);
// float3 finalColor = vec3(r, g, b); float b = dither(texColor.b * brightness, v_uv0);
float3 ditheredColor = dither(brightness, v_uv0); // vec3 ditheredColor = vec3(r,g,b);
float3 finalColor = mix(u_baseColor, texColor, ditheredColor);
gl_FragColor = vec4(finalColor, 1.0); 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

@@ -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);
} }

View File

@@ -42,19 +42,19 @@ float dither(float brightness, vec2 inputUv)
float zRes = dotsTotal; float zRes = dotsTotal;
float invZRes = 1 / zRes; float invZRes = 1 / zRes;
float2 rampLookupUv = vec2((0.5 * invXRes + (1 - invXRes) * brightness), 0.5); vec2 rampLookupUv = vec2((0.5 * invXRes + (1 - invXRes) * brightness), 0.5);
float brightnessCurve = texture2D(s_rampSampler, rampLookupUv).r; float brightnessCurve = texture2D(s_rampSampler, rampLookupUv).r;
// Magic dot frequency calculation // Magic dot frequency calculation
float2 dx = ddx(inputUv); vec2 dx = dFdx(inputUv);
float2 dy = ddy(inputUv); vec2 dy = dFdy(inputUv);
float2x2 mat = float2x2(dx, dy); mat2 mat = mat2(dx, dy);
float4 vectorized = float4(dx, dy); vec4 vectorized = vec4(dx, dy);
float qq = dot(vectorized, vectorized); float qq = dot(vectorized, vectorized);
float rr = determinant(mat); float rr = determinant(mat);
float discriminantSqr = max(0.0, qq*qq - 4.0*rr*rr); float discriminantSqr = max(0.0, qq*qq - 4.0*rr*rr);
float discriminant = sqrt(discriminantSqr); float discriminant = sqrt(discriminantSqr);
float2 freq = sqrt(float2(qq + discriminant, qq - discriminant) / 2.0); vec2 freq = sqrt(vec2(qq + discriminant, qq - discriminant) / 2.0);
// Figuring out how many dots we want // Figuring out how many dots we want
float scaleExp = exp2(globalScale); float scaleExp = exp2(globalScale);
@@ -66,7 +66,7 @@ float dither(float brightness, vec2 inputUv)
spacing *= brightnessSpacingMultiplier; spacing *= brightnessSpacingMultiplier;
float spacingLog = log2(spacing); float spacingLog = log2(spacing);
int patternScaleLevel = floor(spacingLog); int patternScaleLevel = int(floor(spacingLog));
float patternFractional = spacingLog - patternScaleLevel; float patternFractional = spacingLog - patternScaleLevel;
vec2 uv = inputUv / exp2(patternScaleLevel); vec2 uv = inputUv / exp2(patternScaleLevel);
@@ -89,8 +89,9 @@ void main()
{ {
vec2 uv = vec2(1.0 - v_uv0.x, v_uv0.y); vec2 uv = vec2(1.0 - v_uv0.x, v_uv0.y);
vec3 rawTex = texture2D(s_texColor, uv).xyz; vec3 rawTex = texture2D(s_texColor, uv).xyz;
vec3 col = rawTex * u_dotColor.xyz; // float brightness = lerp(0.5, 0.9, calcBrightnessDirectional(vec3(0.5, 0.3, 1.0), v_normal));
float brightness = lerp(0.5, 0.9, calcBrightnessDirectional(vec3(0.5, 0.3, 1.0), v_normal)); vec3 col = rawTex;
brightness = 1.0; if (uv.x < 0.1 || uv.y < 0.1 || uv.x > 0.9 || uv.y > 0.9) col *= u_dotColor.xyz;
float brightness = 1.0;
gl_FragColor = vec4(col * brightness, 1.0); gl_FragColor = vec4(col * brightness, 1.0);
} }

View File

@@ -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, 0.0)).xyz);
} }

91
src/game/util/HashMap.h Normal file
View File

@@ -0,0 +1,91 @@
#include "bx/hash.h"
#include <cassert>
#include <cstdint>
template <typename T> struct HashMapInsertResult
{
T& Obj = nullptr;
bool ElementExists = false;
};
template <typename T> struct HashMapInsertResultConst
{
const T& Obj = nullptr;
bool ElementExists = false;
};
// BitSize 16 will make the HashTable ~200kb with 65k entries
template <typename T, uint32_t BitSize> struct HashMapCore
{
static constexpr uint32_t SlotCount = (1 << BitSize);
static constexpr uint32_t RequiredDataSize = SlotCount * sizeof(T);
T* Data = nullptr;
uint32_t HashTable[SlotCount]{0};
// External array that should be RequiredDataSize bytes big
void Init(T* data, uint64_t dataSize)
{
Data = data;
assert(data != nullptr);
assert(dataSize == RequiredDataSize);
}
bool Has(uint32_t hash) const
{
return HashTable[FindIdx(hash)] == hash;
}
HashMapInsertResult<T> Get(uint32_t hash)
{
uint16_t idx = FindIdx(hash);
return {Data[idx], HashTable[idx] == hash};
}
const T& Get(uint32_t hash) const
{
uint16_t idx = FindIdx(hash);
return {Data[idx], HashTable[idx] == hash};
}
bool Remove(uint32_t hash); // TODO: requires tombstones so later entries survive
private:
uint16_t FindIdx(uint32_t hash) const
{
uint32_t mask = SlotCount - 1;
uint32_t idx = hash & mask;
uint32_t tableEntry = HashTable[idx];
uint32_t stepCount = 0;
while (tableEntry != hash && tableEntry != 0 && stepCount < UINT16_MAX)
{
idx = (idx + 1) % SlotCount;
tableEntry = HashTable[idx];
++stepCount;
}
assert(stepCount < UINT16_MAX);
return idx;
}
};
namespace HashMapTests
{
struct AAA
{
int X = 3;
int Y = 42;
};
inline void Test()
{
HashMapCore<AAA, 16> testMap;
AAA* backingData = new AAA[testMap.RequiredDataSize]();
testMap.Init(backingData, testMap.RequiredDataSize);
AAA test{};
AAA test2{5, 6};
testMap.Get(bx::hash<bx::HashMurmur3>(test)).Obj = test;
testMap.Get(bx::hash<bx::HashMurmur3>(test2)).Obj = test2;
delete[] backingData;
}
} // namespace HashMapTests

View File

@@ -135,6 +135,7 @@ namespace Gen
header.TypeDataSize = header.TypeCount * sizeof(Meta::TypeDef); header.TypeDataSize = header.TypeCount * sizeof(Meta::TypeDef);
header.MemberDataSize = bx::strLen(Meta::Metadata.MemberNameBuffer); header.MemberDataSize = bx::strLen(Meta::Metadata.MemberNameBuffer);
if (!Write(&Meta::CurrentMetaVersion, sizeof(Meta::CurrentMetaVersion))) return false;
if (!Write(&header, sizeof(header))) return false; if (!Write(&header, sizeof(header))) return false;
for (int32_t i = 0; i < typeBuf.DefCount; ++i) for (int32_t i = 0; i < typeBuf.DefCount; ++i)
{ {
@@ -214,6 +215,15 @@ namespace Gen
template <typename T> bool ReadT(T& data) template <typename T> bool ReadT(T& data)
{ {
uint16_t metadataVersion = 0;
if (!Read(&metadataVersion, sizeof(Meta::CurrentMetaVersion))) return false;
if (metadataVersion != Meta::CurrentMetaVersion)
{
// TODO: upgrade?
LOG_ERROR("Metadata version mismatch: %u != %u", metadataVersion, Meta::CurrentMetaVersion);
return false;
}
EmbeddedTypeInfoHeader header; EmbeddedTypeInfoHeader header;
if (!Read(&header, sizeof(header))) return false; if (!Read(&header, sizeof(header))) return false;

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <cassert>
namespace Gen namespace Gen
{ {
@@ -8,7 +7,7 @@ namespace Gen
struct Deserializer; struct Deserializer;
struct PuzzleElementType struct PuzzleElementType
{ {
static constexpr uint16_t EnumIdx = 0; static constexpr uint16_t TypeIdx = 32;
static constexpr int32_t EntryCount = 8; static constexpr int32_t EntryCount = 8;
enum Enum : uint8_t enum Enum : uint8_t
{ {
@@ -55,9 +54,40 @@ namespace Gen
"#", "#",
}; };
}; };
struct PlacedPuzzleCardFlags
{
static constexpr uint16_t TypeIdx = 33;
static constexpr int32_t EntryCount = 2;
enum Enum : uint8_t
{
None = 0,
Locked = 1 << 0,
};
static constexpr char EntryNames[EntryCount][64]
{
"None",
"Locked",
};
};
inline PlacedPuzzleCardFlags::Enum operator| (const PlacedPuzzleCardFlags::Enum& a, const PlacedPuzzleCardFlags::Enum& b)
{
return a | b;
}
inline PlacedPuzzleCardFlags::Enum operator& (const PlacedPuzzleCardFlags::Enum& a, const PlacedPuzzleCardFlags::Enum& b)
{
return a & b;
}
inline PlacedPuzzleCardFlags::Enum operator|= (PlacedPuzzleCardFlags::Enum& a, const PlacedPuzzleCardFlags::Enum& b)
{
return a |= b;
}
inline PlacedPuzzleCardFlags::Enum operator&= (PlacedPuzzleCardFlags::Enum& a, const PlacedPuzzleCardFlags::Enum& b)
{
return a &= b;
}
struct EMaterial struct EMaterial
{ {
static constexpr uint16_t EnumIdx = 1; static constexpr uint16_t TypeIdx = 34;
static constexpr int32_t EntryCount = 2; static constexpr int32_t EntryCount = 2;
enum Enum : int32_t enum Enum : int32_t
{ {
@@ -142,21 +172,33 @@ namespace Gen
int8_t X = {}; int8_t X = {};
int8_t Y = {}; int8_t Y = {};
}; };
struct StaticPuzzleCard struct CardSocket
{ {
static constexpr uint16_t TypeIdx = 22; static constexpr uint16_t TypeIdx = 22;
ModelHandle Model = {};
uint8_t ConnectionDirection = {};
};
struct StaticPuzzleCard
{
static constexpr uint16_t TypeIdx = 23;
PuzzleElementType::Enum Elements[4] = {}; PuzzleElementType::Enum Elements[4] = {};
ModelHandle ModelHandle = {}; ModelHandle BaseModelHandle = {};
ModelHandle NorthCoverHandle = {};
ModelHandle EastCoverHandle = {};
ModelHandle SouthCoverHandle = {};
ModelHandle WestCoverHandle = {};
CardSocket Sockets[16] = {};
TextureHandle ModelTextureHandle = {};
TextureHandle BoardTextureHandle = {}; TextureHandle BoardTextureHandle = {};
}; };
struct StaticPuzzleCardHandle struct StaticPuzzleCardHandle
{ {
static constexpr uint16_t TypeIdx = 23; static constexpr uint16_t TypeIdx = 24;
uint16_t Idx = UINT16_MAX; uint16_t Idx = UINT16_MAX;
}; };
struct PuzzleVisualSettings struct PuzzleVisualSettings
{ {
static constexpr uint16_t TypeIdx = 24; static constexpr uint16_t TypeIdx = 25;
Vec4 TileBaseColor = {}; Vec4 TileBaseColor = {};
Vec4 TileDotColor = {}; Vec4 TileDotColor = {};
Vec3 Test = {}; Vec3 Test = {};
@@ -164,28 +206,28 @@ namespace Gen
}; };
struct StaticPuzzleData struct StaticPuzzleData
{ {
static constexpr uint16_t TypeIdx = 25; static constexpr uint16_t TypeIdx = 26;
StaticPuzzleCard Cards[64] = {}; StaticPuzzleCard Cards[64] = {};
PuzzleVisualSettings Visuals = {}; PuzzleVisualSettings Visuals = {};
}; };
struct PuzzleCardStack struct PuzzleCardStack
{ {
static constexpr uint16_t TypeIdx = 26; static constexpr uint16_t TypeIdx = 27;
StaticPuzzleCardHandle RefCard = {}; StaticPuzzleCardHandle RefCard = {};
uint8_t MaxAvailableCount = {}; uint8_t MaxAvailableCount = {};
uint8_t UsedCount = {}; uint8_t UsedCount = {};
}; };
struct PlacedPuzzleCard struct PlacedPuzzleCard
{ {
static constexpr uint16_t TypeIdx = 27; static constexpr uint16_t TypeIdx = 28;
StaticPuzzleCardHandle RefCard = {}; StaticPuzzleCardHandle RefCard = {};
PuzPos Position = {}; PuzPos Position = {};
uint8_t Rotation = {}; uint8_t Rotation = {};
bool IsLocked = {}; PlacedPuzzleCardFlags::Enum Flags = {};
}; };
struct PuzzleData struct PuzzleData
{ {
static constexpr uint16_t TypeIdx = 28; static constexpr uint16_t TypeIdx = 29;
uint16_t ID = {}; uint16_t ID = {};
char PuzzleName[64] = {}; char PuzzleName[64] = {};
uint8_t WidthTiles = {}; uint8_t WidthTiles = {};
@@ -193,13 +235,14 @@ namespace Gen
uint32_t AvailableCardCount = {}; uint32_t AvailableCardCount = {};
PuzzleCardStack AvailableCards[16] = {}; PuzzleCardStack AvailableCards[16] = {};
PlacedPuzzleCard PlacedCards[256] = {}; PlacedPuzzleCard PlacedCards[256] = {};
PlacedPuzzleCard InitialPlacedCards[256] = {};
PuzzleElementType::Enum BackgroundTiles[1024] = {}; PuzzleElementType::Enum BackgroundTiles[1024] = {};
uint32_t GoalPositionCount = {}; uint32_t GoalPositionCount = {};
PuzPos GoalPositions[16] = {}; PuzPos GoalPositions[16] = {};
}; };
struct SavedEntityRenderData struct SavedEntityRenderData
{ {
static constexpr uint16_t TypeIdx = 29; static constexpr uint16_t TypeIdx = 30;
Vec4 BaseColor = {}; Vec4 BaseColor = {};
Vec4 HighlightColor = {}; Vec4 HighlightColor = {};
Transform TF = {}; Transform TF = {};
@@ -208,8 +251,20 @@ namespace Gen
ModelHandle Model = {}; ModelHandle Model = {};
bool Visible = {}; bool Visible = {};
}; };
struct SavedPlayerConfig
{
static constexpr uint16_t TypeIdx = 31;
SavedEntityRenderData TabletBackgroundRenderData = {};
SavedEntityRenderData TabletStatusRenderData = {};
TextureHandle TabletStatusNotSolvedTexture = {};
TextureHandle TabletStatusSolvedTexture = {};
SavedEntityRenderData TabletResetRenderData = {};
SavedEntityRenderData BackgroundLevelRenderData[16] = {};
};
bool Save(const PuzzleElementType::Enum* obj, uint32_t count, Serializer& serializer); bool Save(const PuzzleElementType::Enum* obj, uint32_t count, Serializer& serializer);
bool Load(PuzzleElementType::Enum* obj, uint32_t count, Deserializer& serializer); bool Load(PuzzleElementType::Enum* obj, uint32_t count, Deserializer& serializer);
bool Save(const PlacedPuzzleCardFlags::Enum* obj, uint32_t count, Serializer& serializer);
bool Load(PlacedPuzzleCardFlags::Enum* obj, uint32_t count, Deserializer& serializer);
bool Save(const EMaterial::Enum* obj, uint32_t count, Serializer& serializer); bool Save(const EMaterial::Enum* obj, uint32_t count, Serializer& serializer);
bool Load(EMaterial::Enum* obj, uint32_t count, Deserializer& serializer); bool Load(EMaterial::Enum* obj, uint32_t count, Deserializer& serializer);
bool Save(const int8_t* obj, uint32_t count, Serializer& serializer); bool Save(const int8_t* obj, uint32_t count, Serializer& serializer);
@@ -256,6 +311,8 @@ namespace Gen
bool Load(TextureHandle* obj, uint32_t count, Deserializer& serializer); bool Load(TextureHandle* obj, uint32_t count, Deserializer& serializer);
bool Save(const PuzPos* obj, uint32_t count, Serializer& serializer); bool Save(const PuzPos* obj, uint32_t count, Serializer& serializer);
bool Load(PuzPos* obj, uint32_t count, Deserializer& serializer); bool Load(PuzPos* obj, uint32_t count, Deserializer& serializer);
bool Save(const CardSocket* obj, uint32_t count, Serializer& serializer);
bool Load(CardSocket* obj, uint32_t count, Deserializer& serializer);
bool Save(const StaticPuzzleCard* obj, uint32_t count, Serializer& serializer); bool Save(const StaticPuzzleCard* obj, uint32_t count, Serializer& serializer);
bool Load(StaticPuzzleCard* obj, uint32_t count, Deserializer& serializer); bool Load(StaticPuzzleCard* obj, uint32_t count, Deserializer& serializer);
bool Save(const StaticPuzzleCardHandle* obj, uint32_t count, Serializer& serializer); bool Save(const StaticPuzzleCardHandle* obj, uint32_t count, Serializer& serializer);
@@ -272,8 +329,12 @@ namespace Gen
bool Load(PuzzleData* obj, uint32_t count, Deserializer& serializer); bool Load(PuzzleData* obj, uint32_t count, Deserializer& serializer);
bool Save(const SavedEntityRenderData* obj, uint32_t count, Serializer& serializer); bool Save(const SavedEntityRenderData* obj, uint32_t count, Serializer& serializer);
bool Load(SavedEntityRenderData* obj, uint32_t count, Deserializer& serializer); bool Load(SavedEntityRenderData* obj, uint32_t count, Deserializer& serializer);
bool Save(const SavedPlayerConfig* obj, uint32_t count, Serializer& serializer);
bool Load(SavedPlayerConfig* obj, uint32_t count, Deserializer& serializer);
namespace Meta { namespace Meta {
constexpr uint16_t CurrentMetaVersion = 1;
struct StrRef struct StrRef
{ {
uint16_t Offset; uint16_t Offset;
@@ -287,56 +348,51 @@ namespace Gen
char Name[64]{"Dummy"}; char Name[64]{"Dummy"};
uint16_t ChildCount = 0; uint16_t ChildCount = 0;
uint16_t ChildIndices[64]{0}; uint16_t ChildIndices[64]{0};
uint16_t ChildArraySizes[64]{0};
StrRef MemberNameIndices[64]{0}; StrRef MemberNameIndices[64]{0};
}; };
struct EnumDef
{
uint32_t Size = 0;
uint32_t Hash = 0;
};
struct MetadataTable struct MetadataTable
{ {
TypeDef TypeDefinitions[30] TypeDef TypeDefinitions[35]
{ {
TypeDef{sizeof(int8_t), 0, "i8", 0, {}, {}}, TypeDef{sizeof(int8_t), 0, "i8", 0, {}, {}, {}},
TypeDef{sizeof(int16_t), 1, "i16", 0, {}, {}}, TypeDef{sizeof(int16_t), 1, "i16", 0, {}, {}, {}},
TypeDef{sizeof(int32_t), 2, "i32", 0, {}, {}}, TypeDef{sizeof(int32_t), 2, "i32", 0, {}, {}, {}},
TypeDef{sizeof(int64_t), 3, "i64", 0, {}, {}}, TypeDef{sizeof(int64_t), 3, "i64", 0, {}, {}, {}},
TypeDef{sizeof(uint8_t), 4, "u8", 0, {}, {}}, TypeDef{sizeof(uint8_t), 4, "u8", 0, {}, {}, {}},
TypeDef{sizeof(uint16_t), 5, "u16", 0, {}, {}}, TypeDef{sizeof(uint16_t), 5, "u16", 0, {}, {}, {}},
TypeDef{sizeof(uint32_t), 6, "u32", 0, {}, {}}, TypeDef{sizeof(uint32_t), 6, "u32", 0, {}, {}, {}},
TypeDef{sizeof(uint64_t), 7, "u64", 0, {}, {}}, TypeDef{sizeof(uint64_t), 7, "u64", 0, {}, {}, {}},
TypeDef{sizeof(bool), 8, "b", 0, {}, {}}, TypeDef{sizeof(bool), 8, "b", 0, {}, {}, {}},
TypeDef{sizeof(float), 9, "f32", 0, {}, {}}, TypeDef{sizeof(float), 9, "f32", 0, {}, {}, {}},
TypeDef{sizeof(double), 10, "f64", 0, {}, {}}, TypeDef{sizeof(double), 10, "f64", 0, {}, {}, {}},
TypeDef{sizeof(char), 11, "str", 0, {}, {}}, TypeDef{sizeof(char), 11, "str", 0, {}, {}, {}},
TypeDef{sizeof(Vec2), 2667033957, "Vec2", 2, {9, 9}, {{0, 1}, {1, 1}}}, TypeDef{sizeof(Vec2), 2667033957, "Vec2", 2, {9, 9}, {0, 0}, {{0, 1}, {1, 1}}},
TypeDef{sizeof(Vec3), 473740858, "Vec3", 3, {9, 9, 9}, {{2, 1}, {3, 1}, {4, 1}}}, TypeDef{sizeof(Vec3), 473740858, "Vec3", 3, {9, 9, 9}, {0, 0, 0}, {{2, 1}, {3, 1}, {4, 1}}},
TypeDef{sizeof(Vec4), 2507696603, "Vec4", 4, {9, 9, 9, 9}, {{5, 1}, {6, 1}, {7, 1}, {8, 1}}}, TypeDef{sizeof(Vec4), 2507696603, "Vec4", 4, {9, 9, 9, 9}, {0, 0, 0, 0}, {{5, 1}, {6, 1}, {7, 1}, {8, 1}}},
TypeDef{sizeof(Mat3), 3364737048, "Mat3", 1, {9}, {{9, 1}}}, TypeDef{sizeof(Mat3), 3364737048, "Mat3", 1, {9}, {9}, {{9, 1}}},
TypeDef{sizeof(Mat4), 1650094019, "Mat4", 1, {9}, {{10, 1}}}, TypeDef{sizeof(Mat4), 1650094019, "Mat4", 1, {9}, {16}, {{10, 1}}},
TypeDef{sizeof(Transform), 4103530190, "Transform", 5, {16, 16, 13, 16, 13}, {{11, 1}, {12, 2}, {14, 8}, {22, 8}, {30, 5}}}, TypeDef{sizeof(Transform), 4103530190, "Transform", 5, {16, 16, 13, 16, 13}, {0, 0, 0, 0, 0}, {{11, 1}, {12, 2}, {14, 8}, {22, 8}, {30, 5}}},
TypeDef{sizeof(AssetHandle), 2609735487, "AssetHandle", 1, {6}, {{35, 3}}}, TypeDef{sizeof(AssetHandle), 2609735487, "AssetHandle", 1, {6}, {0}, {{35, 3}}},
TypeDef{sizeof(ModelHandle), 298089627, "ModelHandle", 2, {5, 18}, {{38, 8}, {46, 5}}}, TypeDef{sizeof(ModelHandle), 298089627, "ModelHandle", 2, {5, 18}, {0, 0}, {{38, 8}, {46, 5}}},
TypeDef{sizeof(TextureHandle), 1633273761, "TextureHandle", 2, {5, 18}, {{51, 10}, {61, 5}}}, TypeDef{sizeof(TextureHandle), 1633273761, "TextureHandle", 2, {5, 18}, {0, 0}, {{51, 10}, {61, 5}}},
TypeDef{sizeof(PuzPos), 1834398141, "PuzPos", 2, {0, 0}, {{66, 1}, {67, 1}}}, TypeDef{sizeof(PuzPos), 1834398141, "PuzPos", 2, {0, 0}, {0, 0}, {{66, 1}, {67, 1}}},
TypeDef{sizeof(StaticPuzzleCard), 3413177578, "StaticPuzzleCard", 3, {0, 19, 20}, {{68, 8}, {76, 11}, {87, 18}}}, TypeDef{sizeof(CardSocket), 2168907571, "CardSocket", 2, {19, 4}, {0, 0}, {{68, 5}, {73, 19}}},
TypeDef{sizeof(StaticPuzzleCardHandle), 1742502768, "StaticPuzzleCardHandle", 1, {5}, {{105, 3}}}, TypeDef{sizeof(StaticPuzzleCard), 537913399, "StaticPuzzleCard", 9, {32, 19, 19, 19, 19, 19, 22, 20, 20}, {4, 0, 0, 0, 0, 0, 16, 0, 0}, {{92, 8}, {100, 15}, {115, 16}, {131, 15}, {146, 16}, {162, 15}, {177, 7}, {184, 18}, {202, 18}}},
TypeDef{sizeof(PuzzleVisualSettings), 2302077481, "PuzzleVisualSettings", 4, {14, 14, 13, 14}, {{108, 13}, {121, 12}, {133, 4}, {137, 16}}}, TypeDef{sizeof(StaticPuzzleCardHandle), 1742502768, "StaticPuzzleCardHandle", 1, {5}, {0}, {{220, 3}}},
TypeDef{sizeof(StaticPuzzleData), 3618749873, "StaticPuzzleData", 2, {22, 24}, {{153, 5}, {158, 7}}}, TypeDef{sizeof(PuzzleVisualSettings), 2302077481, "PuzzleVisualSettings", 4, {14, 14, 13, 14}, {0, 0, 0, 0}, {{223, 13}, {236, 12}, {248, 4}, {252, 16}}},
TypeDef{sizeof(PuzzleCardStack), 53538532, "PuzzleCardStack", 3, {23, 4, 4}, {{165, 7}, {172, 17}, {189, 9}}}, TypeDef{sizeof(StaticPuzzleData), 2637647137, "StaticPuzzleData", 2, {23, 25}, {64, 0}, {{268, 5}, {273, 7}}},
TypeDef{sizeof(PlacedPuzzleCard), 3555575973, "PlacedPuzzleCard", 4, {23, 21, 4, 8}, {{198, 7}, {205, 8}, {213, 8}, {221, 8}}}, TypeDef{sizeof(PuzzleCardStack), 53538532, "PuzzleCardStack", 3, {24, 4, 4}, {0, 0, 0}, {{280, 7}, {287, 17}, {304, 9}}},
TypeDef{sizeof(PuzzleData), 3349686056, "PuzzleData", 10, {5, 11, 4, 4, 6, 26, 27, 0, 6, 21}, {{229, 2}, {231, 10}, {241, 10}, {251, 11}, {262, 18}, {280, 14}, {294, 11}, {305, 15}, {320, 17}, {337, 13}}}, TypeDef{sizeof(PlacedPuzzleCard), 838818025, "PlacedPuzzleCard", 4, {24, 21, 4, 33}, {0, 0, 0, 0}, {{313, 7}, {320, 8}, {328, 8}, {336, 5}}},
TypeDef{sizeof(SavedEntityRenderData), 3172756855, "SavedEntityRenderData", 7, {14, 14, 17, 1, 20, 19, 8}, {{350, 9}, {359, 14}, {373, 2}, {375, 8}, {383, 7}, {390, 5}, {395, 7}}}, TypeDef{sizeof(PuzzleData), 1562434765, "PuzzleData", 11, {5, 11, 4, 4, 6, 27, 28, 28, 32, 6, 21}, {0, 64, 0, 0, 0, 16, 256, 256, 1024, 0, 16}, {{341, 2}, {343, 10}, {353, 10}, {363, 11}, {374, 18}, {392, 14}, {406, 11}, {417, 18}, {435, 15}, {450, 17}, {467, 13}}},
TypeDef{sizeof(SavedEntityRenderData), 3172756855, "SavedEntityRenderData", 7, {14, 14, 17, 34, 20, 19, 8}, {0, 0, 0, 0, 0, 0, 0}, {{480, 9}, {489, 14}, {503, 2}, {505, 8}, {513, 7}, {520, 5}, {525, 7}}},
TypeDef{sizeof(SavedPlayerConfig), 1710757245, "SavedPlayerConfig", 6, {30, 30, 20, 20, 30, 30}, {0, 0, 0, 0, 0, 16}, {{532, 26}, {558, 22}, {580, 28}, {608, 25}, {633, 21}, {654, 25}}},
TypeDef{sizeof(PuzzleElementType::Enum), 2983807453, "PuzzleElementType", 0, {}, {}, {}},
TypeDef{sizeof(PlacedPuzzleCardFlags::Enum), 2983807453, "PlacedPuzzleCardFlags", 0, {}, {}, {}},
TypeDef{sizeof(EMaterial::Enum), 2024002654, "EMaterial", 0, {}, {}, {}},
}; };
EnumDef EnumDefinitions[2] char MemberNameBuffer[64*64*64]{"xyxyzxyzwMMMMIPositionRotationScaleIdxModelIdxAssetTextureIdxAssetXYModelConnectionDirectionElementsBaseModelHandleNorthCoverHandleEastCoverHandleSouthCoverHandleWestCoverHandleSocketsModelTextureHandleBoardTextureHandleIdxTileBaseColorTileDotColorTestDisabledCardTintCardsVisualsRefCardMaxAvailableCountUsedCountRefCardPositionRotationFlagsIDPuzzleNameWidthTilesHeightTilesAvailableCardCountAvailableCardsPlacedCardsInitialPlacedCardsBackgroundTilesGoalPositionCountGoalPositionsBaseColorHighlightColorTFMaterialTextureModelVisibleTabletBackgroundRenderDataTabletStatusRenderDataTabletStatusNotSolvedTextureTabletStatusSolvedTextureTabletResetRenderDataBackgroundLevelRenderData"};
{
EnumDef{sizeof(PuzzleElementType::Enum), 2983807453},
EnumDef{sizeof(EMaterial::Enum), 2024002654},
};
char MemberNameBuffer[64*64*64]{"xyxyzxyzwMMMMIPositionRotationScaleIdxModelIdxAssetTextureIdxAssetXYElementsModelHandleBoardTextureHandleIdxTileBaseColorTileDotColorTestDisabledCardTintCardsVisualsRefCardMaxAvailableCountUsedCountRefCardPositionRotationIsLockedIDPuzzleNameWidthTilesHeightTilesAvailableCardCountAvailableCardsPlacedCardsBackgroundTilesGoalPositionCountGoalPositionsBaseColorHighlightColorTFMaterialTextureModelVisible"};
}; };
constexpr MetadataTable Metadata; constexpr MetadataTable Metadata;

Binary file not shown.

BIN
src/models/GateDoor.glb LFS Normal file

Binary file not shown.

BIN
src/models/GateWall.glb LFS Normal file

Binary file not shown.

BIN
src/models/channel_cover_big.glb LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/models/river.glb LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/models/z_water_cover.glb LFS Normal file

Binary file not shown.

View File

@@ -3,15 +3,21 @@ $shadersDir = ".\game\shaders"
$outputBaseDir = ".\game\compiled-shaders" $outputBaseDir = ".\game\compiled-shaders"
$includeDir = ".\dependency\bgfx.cmake\bgfx\src" $includeDir = ".\dependency\bgfx.cmake\bgfx\src"
function CompileForAPI {
param ([string]$API, [string]$ShaderProfile)
$outDir = "$outputBaseDir\$API\$DirectoryName"
New-Item -ItemType Directory -Path $outDir -Force -ErrorAction Stop | Out-Null
Write-Host "Frag: $API $ShaderProfile"
& $shadercPath -f "$DirectoryFull\vert.sc" -o "$outDir\vert.bin" -i $includeDir --type v --platform windows --profile $ShaderProfile
Write-Host "Vert: $API $ShaderProfile"
& $shadercPath -f "$DirectoryFull\frag.sc" -o "$outDir\frag.bin" -i $includeDir --type f --platform windows --profile $ShaderProfile
}
function Process-Directory { function Process-Directory {
param ([string]$DirectoryFull, [string]$DirectoryName) param ([string]$DirectoryFull, [string]$DirectoryName)
Write-Host "Dir: $DirectoryName" Write-Host "Dir: $DirectoryName"
$outDir = "$outputBaseDir\dx11\$DirectoryName" CompileForAPI -API "dx11" -ShaderProfile "s_5_0"
New-Item -ItemType Directory -Path $outDir -Force -ErrorAction Stop | Out-Null CompileForAPI -API "glsl" -ShaderProfile "430"
Write-Host "Frag"
& $shadercPath -f "$DirectoryFull\vert.sc" -o "$outDir\vert.bin" -i $includeDir --type v --platform windows --profile s_5_0
Write-Host "Vert"
& $shadercPath -f "$DirectoryFull\frag.sc" -o "$outDir\frag.bin" -i $includeDir --type f --platform windows --profile s_5_0
} }
$subdirectories = Get-ChildItem -Path $shadersDir -Directory -Recurse -ErrorAction Stop $subdirectories = Get-ChildItem -Path $shadersDir -Directory -Recurse -ErrorAction Stop

BIN
src/textures/notsolved.ktx LFS Normal file

Binary file not shown.

BIN
src/textures/reset.ktx LFS Normal file

Binary file not shown.

BIN
src/textures/solved.ktx LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

24
tools/radsession.rad Normal file
View File

@@ -0,0 +1,24 @@
// raddbg 0.9.19 project file
recent_file: path: "../src/game/level.cpp"
recent_file: path: "../src/game/puzzle.cpp"
recent_file: path: "../src/dependency/bgfx.cmake/bx/src/debug.cpp"
recent_file: path: "../src/game/entity.h"
recent_file: path: "../src/game/ui.cpp"
recent_file: path: "../src/game/Log.cpp"
recent_file: path: "../src/game/rendering/dither.cpp"
recent_file: path: "../src/game/rendering/rendering.cpp"
recent_file: path: "../src/gen/generated.cpp"
recent_file: path: "../src/engine/main.cpp"
recent_file: path: "../src/game/setup.cpp"
recent_file: path: "../src/dependency/imgui/imgui_widgets.cpp"
recent_file: path: "../src/game/tools.cpp"
recent_file: path: "../src/gen/def.h"
recent_file: path: "../src/game/global.cpp"
target:
{
executable: "../src/cmake-build/PuzGameEngine.exe"
working_directory: "../src"
enabled: 1
debug_subprocesses: 0
}

Binary file not shown.

Binary file not shown.