Skip to content

Commit

Permalink
feat: Parse navmesh and geometry
Browse files Browse the repository at this point in the history
  • Loading branch information
Yamashi committed Feb 3, 2022
1 parent e6d8d28 commit 80c70b5
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 8 deletions.
6 changes: 6 additions & 0 deletions Code/components/es_loader/RecordCollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Records/NPC.h"
#include "Records/REFR.h"
#include "Records/GMST.h"
#include "Records/NAVM.h"
#include "Records/WRLD.h"

class RecordCollection
Expand Down Expand Up @@ -48,6 +49,10 @@ class RecordCollection
{
return m_worlds[aFormId];
}
NAVM& GetNavMeshById(uint32_t aFormId) noexcept
{
return m_navMeshes[aFormId];
}

private:
Map<uint32_t, Record> m_allRecords{};
Expand All @@ -57,4 +62,5 @@ class RecordCollection
Map<uint32_t, CONT> m_containers{};
Map<uint32_t, GMST> m_gameSettings{};
Map<uint32_t, WRLD> m_worlds{};
Map<uint32_t, NAVM> m_navMeshes{};
};
71 changes: 70 additions & 1 deletion Code/components/es_loader/Records/Chunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,73 @@ DNAM::DNAM(Buffer::Reader& aReader)
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_waterLevel), 4);
}

} // namespace
NVNM::NVNM(Buffer::Reader& aReader)
{
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_unknown), 4);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_locactionMarker), 4);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_worldSpaceId), 4);
if (m_worldSpaceId == 0)
{
uint32_t id = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&id), 4);
m_cellId = id;
}
else
{
int16_t tmp = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&tmp), 2);
m_gridY = tmp;

aReader.ReadBytes(reinterpret_cast<uint8_t*>(&tmp), 2);
m_gridX = tmp;
}
int32_t vertexCount = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&vertexCount), 4);
m_vertices.resize(vertexCount);
for (auto& vertex : m_vertices)
{
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&vertex), sizeof(vertex));
}

int32_t triangleCount = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&triangleCount), 4);
m_triangles.resize(triangleCount);
for (auto& tri : m_triangles)
{
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&tri), sizeof(tri));
}

int32_t connectionCount = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&connectionCount), 4);
m_connections.resize(connectionCount);
for (auto& connection : m_connections)
{
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&connection.m_unk), sizeof(connection.m_unk));
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&connection.m_navMeshId), sizeof(connection.m_navMeshId));
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&connection.tri), sizeof(connection.tri));
}

int32_t doorCount = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&doorCount), 4);
m_doorTris.resize(doorCount);
for (auto& doorTri : m_doorTris)
{
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&doorTri.tri), sizeof(doorTri.tri));
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&doorTri.m_unk), sizeof(doorTri.m_unk));
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&doorTri.m_doorId), sizeof(doorTri.m_doorId));
}

int32_t coverTriangleCount = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&coverTriangleCount), 4);
m_coverTris.resize(coverTriangleCount);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(m_coverTris.data()), sizeof(int16_t) * m_coverTris.size());

aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_divisor), 4);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_maxDistance), sizeof(m_maxDistance));
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_min), sizeof(m_min));
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_max), sizeof(m_max));

// Missing triangle divisor but we don't care about it
}

} // namespace Chunks
55 changes: 55 additions & 0 deletions Code/components/es_loader/Records/Chunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,59 @@ struct DNAM
float m_waterLevel;
};

struct NVNM
{
NVNM()
{
}
NVNM(Buffer::Reader& aReader);

struct Tri
{
int16_t m_vertex0;
int16_t m_vertex1;
int16_t m_vertex2;

int16_t m_edge0;
int16_t m_edge1;
int16_t m_edge2;

int16_t m_coverMarker;
int16_t m_coverFlags;
};

struct Connection
{
uint32_t m_unk;
uint32_t m_navMeshId;
int16_t tri;
};

struct Door
{
int16_t tri;
uint32_t m_unk;
uint32_t m_doorId;
};

uint32_t m_unknown;
uint32_t m_locactionMarker;
uint32_t m_worldSpaceId;
std::optional<uint32_t> m_cellId;
std::optional<int16_t> m_gridX;
std::optional<int16_t> m_gridY;
Vector<glm::vec3> m_vertices;
Vector<Tri> m_triangles;
Vector<Connection> m_connections;
Vector<Door> m_doorTris;
Vector<int16_t> m_coverTris;
uint32_t m_divisor;
glm::vec2 m_maxDistance;
glm::vec3 m_min;
glm::vec3 m_max;
};

static_assert(sizeof(glm::vec3) == 12);
static_assert(sizeof(NVNM::Tri) == 16);

} // namespace
15 changes: 15 additions & 0 deletions Code/components/es_loader/Records/NAVM.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "NAVM.h"

#include <ESLoader.h>

void NAVM::ParseChunks(NAVM &aSourceRecord, Map<uint8_t, uint32_t>& aParentToFormIdPrefix) noexcept
{
aSourceRecord.IterateChunks([&](ChunkId aChunkId, Buffer::Reader& aReader) {
switch (aChunkId)
{
case ChunkId::NVNM_ID:
m_navMesh = Chunks::NVNM{aReader};
break;
}
});
}
15 changes: 15 additions & 0 deletions Code/components/es_loader/Records/NAVM.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include "Record.h"
#include "Chunks.h"

// https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/NAVM
class NAVM : public Record
{
public:
static constexpr FormEnum kType = FormEnum::NAVM;

Chunks::NVNM m_navMesh;

void ParseChunks(NAVM& aSourceRecord, Map<uint8_t, uint32_t>& aParentToFormIdPrefix) noexcept;
};
7 changes: 6 additions & 1 deletion Code/components/es_loader/Records/TESFileRecordTypes.inl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ enum class FormEnum : uint32_t
DECLARE_FORM_TYPE(CONT),
DECLARE_FORM_TYPE(NPC_),
DECLARE_FORM_TYPE(GMST),
DECLARE_FORM_TYPE(WRLD)
DECLARE_FORM_TYPE(WRLD),
DECLARE_FORM_TYPE(NAVM),
DECLARE_FORM_TYPE(PGRE),
DECLARE_FORM_TYPE(PHZD),
DECLARE_FORM_TYPE(LAND),
DECLARE_FORM_TYPE(INFO),
};

enum class ChunkId : uint32_t
Expand Down
5 changes: 5 additions & 0 deletions Code/components/es_loader/Records/WRLD.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "WRLD.h"

#include <ESLoader.h>
#include <iostream>

void WRLD::ParseChunks(WRLD& aSourceRecord, Map<uint8_t, uint32_t>& aParentToFormIdPrefix) noexcept
{
Expand Down Expand Up @@ -34,3 +35,7 @@ void WRLD::ParseChunks(WRLD& aSourceRecord, Map<uint8_t, uint32_t>& aParentToFor
}
});
}

void WRLD::ParseGRUP() noexcept
{
}
1 change: 1 addition & 0 deletions Code/components/es_loader/Records/WRLD.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ class WRLD : public Record
float m_lodMultiplier;

void ParseChunks(WRLD& aSourceRecord, Map<uint8_t, uint32_t>& aParentToFormIdPrefix) noexcept;
void ParseGRUP() noexcept;
};
25 changes: 23 additions & 2 deletions Code/components/es_loader/TESFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ bool TESFile::ReadGroupOrRecord(Buffer::Reader& aReader, RecordCollection& aReco
WRLD parsedRecord = CopyAndParseRecord<WRLD>(pRecord);
aRecordCollection.m_worlds[parsedRecord.GetFormId()] = parsedRecord;
}
case FormEnum::NAVM: {
NAVM parsedRecord = CopyAndParseRecord<NAVM>(pRecord);
aRecordCollection.m_navMeshes[parsedRecord.GetFormId()] = parsedRecord;

}
}

//pRecord->DiscoverChunks();
Expand All @@ -150,21 +155,37 @@ bool TESFile::ReadGroupOrRecord(Buffer::Reader& aReader, RecordCollection& aReco
return true;
}

template <typename T>
concept ExpectsGRUP = requires(T t)
{
&T::ParseGRUP;
};

template <class T>
T TESFile::CopyAndParseRecord(Record* pRecordHeader)
{
T* pRecord = reinterpret_cast<T*>(pRecordHeader);

T parsedRecord;
parsedRecord.CopyRecordData(*pRecord);

parsedRecord.SetBaseId(TESFile::GetFormIdPrefix(pRecord->GetFormId(), m_parentToFormIdPrefix));

parsedRecord.ParseChunks(*pRecord, m_parentToFormIdPrefix);

// If the record expects a subgroup right after, parse it? Or do we not care since we load everything?
if constexpr (ExpectsGRUP<T>)
{
// ParseGRUP(pRecord, parsedRecord);
}

return parsedRecord;
}

template <class T>
void TESFile::ParseGRUP(Record* pRecordHeader, T& aRecord)
{
//aRecord.ParseGRUP();
}

uint32_t TESFile::GetFormIdPrefix(uint32_t aFormId, Map<uint8_t, uint32_t>& aParentToFormIdPrefix) noexcept
{
auto baseId = (uint8_t)(aFormId >> 24);
Expand Down
6 changes: 4 additions & 2 deletions Code/components/es_loader/TESFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <Records/TES4.h>
#include <Records/GMST.h>


class TESFile
{
public:
Expand All @@ -25,8 +26,9 @@ class TESFile
private:
bool ReadGroupOrRecord(Buffer::Reader& aReader, RecordCollection& aRecordCollection) noexcept;

template <class T>
T CopyAndParseRecord(Record* pRecordHeader);
template <class T> T CopyAndParseRecord(Record* pRecordHeader);

template <class T> void ParseGRUP(Record* pRecordHeader, T& aRecord);

String m_filename = "";
Buffer m_buffer{};
Expand Down
1 change: 1 addition & 0 deletions Code/components/es_loader/stdafx.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <TiltedCore/Stl.hpp>
#include <TiltedCore/Buffer.hpp>
#include <TiltedCore/Serialization.hpp>
#include <glm/glm.hpp>

#include <spdlog/spdlog.h>

Expand Down
4 changes: 2 additions & 2 deletions Code/components/es_loader/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ component("ESLoader")
set_pcxxheader("stdafx.h")
add_headerfiles("stdafx.h", {prefixdir = "ESLoader"})
add_files("stdafx.cpp")
add_packages("zlib")
add_packages("zlib", "glm")

unittest("ESLoader")
set_pcxxheader("stdafx.h")
add_headerfiles("stdafx.h", {prefixdir = "ESLoader"})
add_files("stdafx.cpp")
add_packages("zlib")
add_packages("zlib", "glm")

0 comments on commit 80c70b5

Please sign in to comment.