feat: raw terrain parsing for scene data

Replace old dNavigation/dTerrain raw parser with new Raw module in
dZoneManager. Parse heightmaps, color maps, and scene maps from .raw
files to determine which scene a position belongs to. Build scene
adjacency graph from terrain data and scene transitions.

Adds NiColor type, SceneColor lookup table, eSceneType enum, terrain
mesh generation with OBJ export, and debug slash commands for scene
visualization.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-06-24 18:31:28 -05:00
parent 35337291fa
commit feeaf339d4
23 changed files with 1404 additions and 316 deletions

View File

@@ -1,11 +1,5 @@
set(DNAVIGATION_SOURCES "dNavMesh.cpp")
add_subdirectory(dTerrain)
foreach(file ${DNAVIGATIONS_DTERRAIN_SOURCES})
set(DNAVIGATION_SOURCES ${DNAVIGATION_SOURCES} "dTerrain/${file}")
endforeach()
add_library(dNavigation OBJECT ${DNAVIGATION_SOURCES})
target_include_directories(dNavigation PUBLIC "."
PRIVATE

View File

@@ -1,6 +1,5 @@
#include "dNavMesh.h"
#include "RawFile.h"
#include "Game.h"
#include "Logger.h"

View File

@@ -1,3 +0,0 @@
set(DNAVIGATIONS_DTERRAIN_SOURCES "RawFile.cpp"
"RawChunk.cpp"
"RawHeightMap.cpp" PARENT_SCOPE)

View File

@@ -1,93 +0,0 @@
#include "RawChunk.h"
#include "BinaryIO.h"
#include "RawMesh.h"
#include "RawHeightMap.h"
RawChunk::RawChunk(std::ifstream& stream) {
// Read the chunk index and info
BinaryIO::BinaryRead(stream, m_ChunkIndex);
BinaryIO::BinaryRead(stream, m_Width);
BinaryIO::BinaryRead(stream, m_Height);
BinaryIO::BinaryRead(stream, m_X);
BinaryIO::BinaryRead(stream, m_Z);
m_HeightMap = new RawHeightMap(stream, m_Height, m_Width);
// We can just skip the rest of the data so we can read the next chunks, we don't need anymore data
// Possible overflow here? TODO make reasonable upper bound or confirm big numbers arent necessary to have
uint32_t colorMapSize;
BinaryIO::BinaryRead(stream, colorMapSize);
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (colorMapSize * colorMapSize * 4));
uint32_t lightmapSize;
BinaryIO::BinaryRead(stream, lightmapSize);
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (lightmapSize));
uint32_t colorMapSize2;
BinaryIO::BinaryRead(stream, colorMapSize2);
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (colorMapSize2 * colorMapSize2 * 4));
uint8_t unknown;
BinaryIO::BinaryRead(stream, unknown);
uint32_t blendmapSize;
BinaryIO::BinaryRead(stream, blendmapSize);
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (blendmapSize));
uint32_t pointSize;
BinaryIO::BinaryRead(stream, pointSize);
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (pointSize * 9 * 4));
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (colorMapSize * colorMapSize));
uint32_t endCounter;
BinaryIO::BinaryRead(stream, endCounter);
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (endCounter * 2));
if (endCounter != 0) {
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (32));
for (int i = 0; i < 0x10; i++) {
uint16_t finalCountdown;
BinaryIO::BinaryRead(stream, finalCountdown);
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (finalCountdown * 2));
}
}
// Generate our mesh/geo data for this chunk
this->GenerateMesh();
}
RawChunk::~RawChunk() {
if (m_Mesh) delete m_Mesh;
if (m_HeightMap) delete m_HeightMap;
}
void RawChunk::GenerateMesh() {
RawMesh* meshData = new RawMesh();
for (int i = 0; i < m_Width; ++i) {
for (int j = 0; j < m_Height; ++j) {
float y = *std::next(m_HeightMap->m_FloatMap.begin(), m_Width * i + j);
meshData->m_Vertices.push_back(NiPoint3(i, y, j));
if (i == 0 || j == 0) continue;
meshData->m_Triangles.push_back(m_Width * i + j);
meshData->m_Triangles.push_back(m_Width * i + j - 1);
meshData->m_Triangles.push_back(m_Width * (i - 1) + j - 1);
meshData->m_Triangles.push_back(m_Width * (i - 1) + j - 1);
meshData->m_Triangles.push_back(m_Width * (i - 1) + j);
meshData->m_Triangles.push_back(m_Width * i + j);
}
}
m_Mesh = meshData;
}

View File

@@ -1,24 +0,0 @@
#pragma once
#include <cstdint>
#include <fstream>
struct RawMesh;
class RawHeightMap;
class RawChunk {
public:
RawChunk(std::ifstream& stream);
~RawChunk();
void GenerateMesh();
uint32_t m_ChunkIndex;
uint32_t m_Width;
uint32_t m_Height;
float m_X;
float m_Z;
RawHeightMap* m_HeightMap;
RawMesh* m_Mesh;
};

View File

@@ -1,84 +0,0 @@
#include "RawFile.h"
#include "BinaryIO.h"
#include "RawChunk.h"
#include "RawMesh.h"
#include "RawHeightMap.h"
RawFile::RawFile(std::string fileName) {
if (!BinaryIO::DoesFileExist(fileName)) return;
std::ifstream file(fileName, std::ios::binary);
// Read header
BinaryIO::BinaryRead(file, m_Version);
BinaryIO::BinaryRead(file, m_Padding);
BinaryIO::BinaryRead(file, m_ChunkCount);
BinaryIO::BinaryRead(file, m_Width);
BinaryIO::BinaryRead(file, m_Height);
if (m_Version < 0x20) {
return; // Version is too old to be supported
}
// Read in chunks
m_Chunks = {};
for (uint32_t i = 0; i < m_ChunkCount; i++) {
RawChunk* chunk = new RawChunk(file);
m_Chunks.push_back(chunk);
}
m_FinalMesh = new RawMesh();
this->GenerateFinalMeshFromChunks();
}
RawFile::~RawFile() {
if (m_FinalMesh) delete m_FinalMesh;
for (const auto* item : m_Chunks) {
if (item) delete item;
}
}
void RawFile::GenerateFinalMeshFromChunks() {
uint32_t lenOfLastChunk = 0; // index of last vert set in the last chunk
for (const auto& chunk : m_Chunks) {
for (const auto& vert : chunk->m_Mesh->m_Vertices) {
auto tempVert = vert;
// Scale X and Z by the chunk's position in the world
// Scale Y by the chunk's heightmap scale factor
tempVert.SetX(tempVert.GetX() + (chunk->m_X / chunk->m_HeightMap->m_ScaleFactor));
tempVert.SetY(tempVert.GetY() / chunk->m_HeightMap->m_ScaleFactor);
tempVert.SetZ(tempVert.GetZ() + (chunk->m_Z / chunk->m_HeightMap->m_ScaleFactor));
// Then scale it again for some reason
tempVert *= chunk->m_HeightMap->m_ScaleFactor;
m_FinalMesh->m_Vertices.push_back(tempVert);
}
for (const auto& tri : chunk->m_Mesh->m_Triangles) {
m_FinalMesh->m_Triangles.push_back(tri + lenOfLastChunk);
}
lenOfLastChunk += chunk->m_Mesh->m_Vertices.size();
}
}
void RawFile::WriteFinalMeshToOBJ(std::string path) {
std::ofstream file(path);
for (const auto& v : m_FinalMesh->m_Vertices) {
file << "v " << v.x << ' ' << v.y << ' ' << v.z << '\n';
}
for (int i = 0; i < m_FinalMesh->m_Triangles.size(); i += 3) {
file << "f " << *std::next(m_FinalMesh->m_Triangles.begin(), i) + 1 << ' ' << *std::next(m_FinalMesh->m_Triangles.begin(), i + 1) + 1 << ' ' << *std::next(m_FinalMesh->m_Triangles.begin(), i + 2) + 1 << '\n';
}
}

View File

@@ -1,28 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <cstdint>
class RawChunk;
struct RawMesh;
class RawFile {
public:
RawFile(std::string filePath);
~RawFile();
private:
void GenerateFinalMeshFromChunks();
void WriteFinalMeshToOBJ(std::string path);
uint8_t m_Version;
uint16_t m_Padding;
uint32_t m_ChunkCount;
uint32_t m_Width;
uint32_t m_Height;
std::vector<RawChunk*> m_Chunks;
RawMesh* m_FinalMesh = nullptr;
};

View File

@@ -1,27 +0,0 @@
#include "RawHeightMap.h"
#include "BinaryIO.h"
RawHeightMap::RawHeightMap() {}
RawHeightMap::RawHeightMap(std::ifstream& stream, float height, float width) {
// Read in height map data header and scale
BinaryIO::BinaryRead(stream, m_Unknown1);
BinaryIO::BinaryRead(stream, m_Unknown2);
BinaryIO::BinaryRead(stream, m_Unknown3);
BinaryIO::BinaryRead(stream, m_Unknown4);
BinaryIO::BinaryRead(stream, m_ScaleFactor);
// read all vertices in
for (uint64_t i = 0; i < width * height; i++) {
float value;
BinaryIO::BinaryRead(stream, value);
m_FloatMap.push_back(value);
}
}
RawHeightMap::~RawHeightMap() {
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include <cstdint>
#include <vector>
#include <fstream>
class RawHeightMap {
public:
RawHeightMap();
RawHeightMap(std::ifstream& stream, float height, float width);
~RawHeightMap();
uint32_t m_Unknown1;
uint32_t m_Unknown2;
uint32_t m_Unknown3;
uint32_t m_Unknown4;
float m_ScaleFactor;
std::vector<float> m_FloatMap = {};
};

View File

@@ -1,10 +0,0 @@
#pragma once
#include <vector>
#include "NiPoint3.h"
struct RawMesh {
std::vector<NiPoint3> m_Vertices;
std::vector<uint32_t> m_Triangles;
};