From accdb4f9a17e1d820612ea2685d334c153e867ef Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Wed, 15 Oct 2025 19:56:46 -0500 Subject: [PATCH] Correct scene making, merged the old raw into the new. added option to automatically write the raw obj file Added scene colors to the obj use proper scene colors from hf --- dCommon/NiColor.h | 34 ++ dCommon/dClient/SceneColor.h | 170 ++++++++ dNavigation/CMakeLists.txt | 6 - dNavigation/dNavMesh.cpp | 1 - dNavigation/dTerrain/CMakeLists.txt | 3 - dNavigation/dTerrain/RawChunk.cpp | 92 ---- dNavigation/dTerrain/RawChunk.h | 24 -- dNavigation/dTerrain/RawFile.cpp | 84 ---- dNavigation/dTerrain/RawFile.h | 28 -- dNavigation/dTerrain/RawHeightMap.cpp | 27 -- dNavigation/dTerrain/RawHeightMap.h | 21 - dNavigation/dTerrain/RawMesh.h | 10 - dZoneManager/CMakeLists.txt | 1 + dZoneManager/Raw.cpp | 87 +++- dZoneManager/Raw.h | 13 + dZoneManager/Zone.cpp | 9 + dZoneManager/dZoneManager.cpp | 17 + .../48a84d7fa3b1c27696794fcc62d106d/Raw.cpp | 403 ++++++++++++++++++ resources/worldconfig.ini | 4 + 19 files changed, 720 insertions(+), 314 deletions(-) create mode 100644 dCommon/NiColor.h create mode 100644 dCommon/dClient/SceneColor.h delete mode 100644 dNavigation/dTerrain/CMakeLists.txt delete mode 100644 dNavigation/dTerrain/RawChunk.cpp delete mode 100644 dNavigation/dTerrain/RawChunk.h delete mode 100644 dNavigation/dTerrain/RawFile.cpp delete mode 100644 dNavigation/dTerrain/RawFile.h delete mode 100644 dNavigation/dTerrain/RawHeightMap.cpp delete mode 100644 dNavigation/dTerrain/RawHeightMap.h delete mode 100644 dNavigation/dTerrain/RawMesh.h create mode 100644 enc_temp_folder/48a84d7fa3b1c27696794fcc62d106d/Raw.cpp diff --git a/dCommon/NiColor.h b/dCommon/NiColor.h new file mode 100644 index 00000000..e139606b --- /dev/null +++ b/dCommon/NiColor.h @@ -0,0 +1,34 @@ +#ifndef NICOLOR_H +#define NICOLOR_H + +struct NiColor { + float m_Red; + float m_Green; + float m_Blue; + + NiColor(float red, float green, float blue) : m_Red(red), m_Green(green), m_Blue(blue) {} + NiColor() : NiColor(0.0f, 0.0f, 0.0f) {} + + /* reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ at + * // dead link + * Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * We approximate this with + * + * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * The calculation is to be done in a linear colorspace. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). + */ + float ToXYZ() const { return (m_Red * 0.212671f) + (m_Green * 0.71516f) + (m_Blue * 0.072169f); }; +}; + +#endif // NICOLOR_H \ No newline at end of file diff --git a/dCommon/dClient/SceneColor.h b/dCommon/dClient/SceneColor.h new file mode 100644 index 00000000..bdee3024 --- /dev/null +++ b/dCommon/dClient/SceneColor.h @@ -0,0 +1,170 @@ +#ifndef SCENE_COLOR_H +#define SCENE_COLOR_H + +#include "NiColor.h" +#include + +class SceneColor { +public: + SceneColor() { + TEMPLATE_COLORS.resize(146); + + // these are not random values, they are the actual template colors used by the game + TEMPLATE_COLORS[0] = NiColor(0.5019608f, 0.5019608f, 0.5019608f); + TEMPLATE_COLORS[1] = NiColor(1.0f, 0.0f, 0.0f); + TEMPLATE_COLORS[2] = NiColor(0.0f, 1.0f, 0.0f); + TEMPLATE_COLORS[3] = NiColor(0.0f, 0.0f, 1.0f); + TEMPLATE_COLORS[4] = NiColor(1.0f, 1.0f, 0.0f); + TEMPLATE_COLORS[5] = NiColor(1.0f, 0.0f, 1.0f); + TEMPLATE_COLORS[6] = NiColor(0.0f, 1.0f, 1.0f); + TEMPLATE_COLORS[7] = NiColor(0.5019608f, 0.0f, 1.0f); + TEMPLATE_COLORS[8] = NiColor(1.0f, 0.5019608f, 0.0f); + TEMPLATE_COLORS[9] = NiColor(1.0f, 0.5019608f, 0.5019608f); + TEMPLATE_COLORS[10] = NiColor(0.5019608f, 0.2509804f, 0.0f); + TEMPLATE_COLORS[11] = NiColor(0.5019608f, 0.0f, 0.2509804f); + TEMPLATE_COLORS[12] = NiColor(0.0f, 0.5019608f, 0.2509804f); + TEMPLATE_COLORS[13] = NiColor(0.2509804f, 0.0f, 0.5019608f); + TEMPLATE_COLORS[14] = NiColor(0.8745098f, 0.0f, 0.2509804f); + TEMPLATE_COLORS[15] = NiColor(0.2509804f, 0.8745098f, 0.5019608f); + TEMPLATE_COLORS[16] = NiColor(1.0f, 0.7490196f, 0.0f); + TEMPLATE_COLORS[17] = NiColor(1.0f, 0.2509804f, 0.0627451f); + TEMPLATE_COLORS[18] = NiColor(0.2509804f, 0.0f, 0.8745098f); + TEMPLATE_COLORS[19] = NiColor(0.7490196f, 0.0627451f, 0.0627451f); + TEMPLATE_COLORS[20] = NiColor(0.0627451f, 0.7490196f, 0.0627451f); + TEMPLATE_COLORS[21] = NiColor(1.0f, 0.5019608f, 1.0f); + TEMPLATE_COLORS[22] = NiColor(0.9372549f, 0.8705882f, 0.8039216f); + TEMPLATE_COLORS[23] = NiColor(0.8039216f, 0.5843138f, 0.4588235f); + TEMPLATE_COLORS[24] = NiColor(0.9921569f, 0.8509804f, 0.7098039f); + TEMPLATE_COLORS[25] = NiColor(0.4705882f, 0.8588235f, 0.8862745f); + TEMPLATE_COLORS[26] = NiColor(0.5294118f, 0.6627451f, 0.4196078f); + TEMPLATE_COLORS[27] = NiColor(1.0f, 0.6431373f, 0.454902f); + TEMPLATE_COLORS[28] = NiColor(0.9803922f, 0.9058824f, 0.7098039f); + TEMPLATE_COLORS[29] = NiColor(0.6235294f, 0.5058824f, 0.4392157f); + TEMPLATE_COLORS[30] = NiColor(0.9921569f, 0.4862745f, 0.4313726f); + TEMPLATE_COLORS[31] = NiColor(0.0f, 0.0f, 0.0f); + TEMPLATE_COLORS[32] = NiColor(0.6745098f, 0.8980392f, 0.9333333f); + TEMPLATE_COLORS[33] = NiColor(0.1215686f, 0.4588235f, 0.9960784f); + TEMPLATE_COLORS[34] = NiColor(0.6352941f, 0.6352941f, 0.8156863f); + TEMPLATE_COLORS[35] = NiColor(0.4f, 0.6f, 0.8f); + TEMPLATE_COLORS[36] = NiColor(0.05098039f, 0.5960785f, 0.7294118f); + TEMPLATE_COLORS[37] = NiColor(0.4509804f, 0.4f, 0.7411765f); + TEMPLATE_COLORS[38] = NiColor(0.8705882f, 0.3647059f, 0.5137255f); + TEMPLATE_COLORS[39] = NiColor(0.7960784f, 0.254902f, 0.3294118f); + TEMPLATE_COLORS[40] = NiColor(0.7058824f, 0.4039216f, 0.3019608f); + TEMPLATE_COLORS[41] = NiColor(1.0f, 0.4980392f, 0.2862745f); + TEMPLATE_COLORS[42] = NiColor(0.9176471f, 0.4941176f, 0.3647059f); + TEMPLATE_COLORS[43] = NiColor(0.6901961f, 0.7176471f, 0.7764706f); + TEMPLATE_COLORS[44] = NiColor(1.0f, 1.0f, 0.6f); + TEMPLATE_COLORS[45] = NiColor(0.1098039f, 0.827451f, 0.6352941f); + TEMPLATE_COLORS[46] = NiColor(1.0f, 0.6666667f, 0.8f); + TEMPLATE_COLORS[47] = NiColor(0.8666667f, 0.2666667f, 0.572549f); + TEMPLATE_COLORS[48] = NiColor(0.1137255f, 0.6745098f, 0.8392157f); + TEMPLATE_COLORS[49] = NiColor(0.7372549f, 0.3647059f, 0.345098f); + TEMPLATE_COLORS[50] = NiColor(0.8666667f, 0.5803922f, 0.4588235f); + TEMPLATE_COLORS[51] = NiColor(0.6039216f, 0.8078431f, 0.9215686f); + TEMPLATE_COLORS[52] = NiColor(1.0f, 0.7372549f, 0.8509804f); + TEMPLATE_COLORS[53] = NiColor(0.9921569f, 0.8588235f, 0.427451f); + TEMPLATE_COLORS[54] = NiColor(0.1686275f, 0.4235294f, 0.7686275f); + TEMPLATE_COLORS[55] = NiColor(0.9372549f, 0.8039216f, 0.7215686f); + TEMPLATE_COLORS[56] = NiColor(0.4313726f, 0.3176471f, 0.3764706f); + TEMPLATE_COLORS[57] = NiColor(0.8078431f, 1.0f, 0.1137255f); + TEMPLATE_COLORS[58] = NiColor(0.427451f, 0.682353f, 0.5058824f); + TEMPLATE_COLORS[59] = NiColor(0.7647059f, 0.3921569f, 0.772549f); + TEMPLATE_COLORS[60] = NiColor(0.8f, 0.4f, 0.4f); + TEMPLATE_COLORS[61] = NiColor(0.9058824f, 0.7764706f, 0.5921569f); + TEMPLATE_COLORS[62] = NiColor(0.9882353f, 0.8509804f, 0.4588235f); + TEMPLATE_COLORS[63] = NiColor(0.6588235f, 0.8941177f, 0.627451f); + TEMPLATE_COLORS[64] = NiColor(0.5843138f, 0.5686275f, 0.5490196f); + TEMPLATE_COLORS[65] = NiColor(0.1098039f, 0.6745098f, 0.4705882f); + TEMPLATE_COLORS[66] = NiColor(0.06666667f, 0.3921569f, 0.7058824f); + TEMPLATE_COLORS[67] = NiColor(0.9411765f, 0.9098039f, 0.5686275f); + TEMPLATE_COLORS[68] = NiColor(1.0f, 0.1137255f, 0.8078431f); + TEMPLATE_COLORS[69] = NiColor(0.6980392f, 0.9254902f, 0.3647059f); + TEMPLATE_COLORS[70] = NiColor(0.3647059f, 0.4627451f, 0.7960784f); + TEMPLATE_COLORS[71] = NiColor(0.7921569f, 0.2156863f, 0.4039216f); + TEMPLATE_COLORS[72] = NiColor(0.2313726f, 0.6901961f, 0.5607843f); + TEMPLATE_COLORS[73] = NiColor(0.9882353f, 0.7058824f, 0.8352941f); + TEMPLATE_COLORS[74] = NiColor(1.0f, 0.9568627f, 0.3098039f); + TEMPLATE_COLORS[75] = NiColor(1.0f, 0.7411765f, 0.5333334f); + TEMPLATE_COLORS[76] = NiColor(0.9647059f, 0.3921569f, 0.6862745f); + TEMPLATE_COLORS[77] = NiColor(0.6666667f, 0.9411765f, 0.8196079f); + TEMPLATE_COLORS[78] = NiColor(0.8039216f, 0.2901961f, 0.2980392f); + TEMPLATE_COLORS[79] = NiColor(0.9294118f, 0.8196079f, 0.6117647f); + TEMPLATE_COLORS[80] = NiColor(0.5921569f, 0.6039216f, 0.6666667f); + TEMPLATE_COLORS[81] = NiColor(0.7843137f, 0.2196078f, 0.3529412f); + TEMPLATE_COLORS[82] = NiColor(0.9372549f, 0.5960785f, 0.6666667f); + TEMPLATE_COLORS[83] = NiColor(0.9921569f, 0.7372549f, 0.7058824f); + TEMPLATE_COLORS[84] = NiColor(0.1019608f, 0.282353f, 0.4627451f); + TEMPLATE_COLORS[85] = NiColor(0.1882353f, 0.7294118f, 0.5607843f); + TEMPLATE_COLORS[86] = NiColor(0.772549f, 0.2941177f, 0.5490196f); + TEMPLATE_COLORS[87] = NiColor(0.09803922f, 0.454902f, 0.8235294f); + TEMPLATE_COLORS[88] = NiColor(0.7294118f, 0.7215686f, 0.4235294f); + TEMPLATE_COLORS[89] = NiColor(1.0f, 0.4588235f, 0.2196078f); + TEMPLATE_COLORS[90] = NiColor(1.0f, 0.1686275f, 0.1686275f); + TEMPLATE_COLORS[91] = NiColor(0.972549f, 0.8352941f, 0.4078431f); + TEMPLATE_COLORS[92] = NiColor(0.9019608f, 0.6588235f, 0.8431373f); + TEMPLATE_COLORS[93] = NiColor(0.254902f, 0.2901961f, 0.2980392f); + TEMPLATE_COLORS[94] = NiColor(1.0f, 0.4313726f, 0.2901961f); + TEMPLATE_COLORS[95] = NiColor(0.1098039f, 0.6627451f, 0.7882353f); + TEMPLATE_COLORS[96] = NiColor(1.0f, 0.8117647f, 0.6705883f); + TEMPLATE_COLORS[97] = NiColor(0.772549f, 0.8156863f, 0.9019608f); + TEMPLATE_COLORS[98] = NiColor(0.9921569f, 0.8666667f, 0.9019608f); + TEMPLATE_COLORS[99] = NiColor(0.08235294f, 0.5019608f, 0.4705882f); + TEMPLATE_COLORS[100] = NiColor(0.9882353f, 0.454902f, 0.9921569f); + TEMPLATE_COLORS[101] = NiColor(0.9686275f, 0.5607843f, 0.654902f); + TEMPLATE_COLORS[102] = NiColor(0.5568628f, 0.2705882f, 0.5215687f); + TEMPLATE_COLORS[103] = NiColor(0.454902f, 0.2588235f, 0.7843137f); + TEMPLATE_COLORS[104] = NiColor(0.6156863f, 0.5058824f, 0.7294118f); + TEMPLATE_COLORS[105] = NiColor(1.0f, 0.2862745f, 0.4235294f); + TEMPLATE_COLORS[106] = NiColor(0.8392157f, 0.5411765f, 0.3490196f); + TEMPLATE_COLORS[107] = NiColor(0.4431373f, 0.2941177f, 0.1372549f); + TEMPLATE_COLORS[108] = NiColor(1.0f, 0.282353f, 0.8156863f); + TEMPLATE_COLORS[109] = NiColor(0.9333333f, 0.1254902f, 0.3019608f); + TEMPLATE_COLORS[110] = NiColor(1.0f, 0.3254902f, 0.2862745f); + TEMPLATE_COLORS[111] = NiColor(0.7529412f, 0.2666667f, 0.5607843f); + TEMPLATE_COLORS[112] = NiColor(0.1215686f, 0.8078431f, 0.7960784f); + TEMPLATE_COLORS[113] = NiColor(0.4705882f, 0.3176471f, 0.6627451f); + TEMPLATE_COLORS[114] = NiColor(1.0f, 0.6078432f, 0.6666667f); + TEMPLATE_COLORS[115] = NiColor(0.9882353f, 0.1568628f, 0.2784314f); + TEMPLATE_COLORS[116] = NiColor(0.4627451f, 1.0f, 0.4784314f); + TEMPLATE_COLORS[117] = NiColor(0.6235294f, 0.8862745f, 0.7490196f); + TEMPLATE_COLORS[118] = NiColor(0.6470588f, 0.4117647f, 0.3098039f); + TEMPLATE_COLORS[119] = NiColor(0.5411765f, 0.4745098f, 0.3647059f); + TEMPLATE_COLORS[120] = NiColor(0.2705882f, 0.8078431f, 0.6352941f); + TEMPLATE_COLORS[121] = NiColor(0.8039216f, 0.772549f, 0.7607843f); + TEMPLATE_COLORS[122] = NiColor(0.5019608f, 0.854902f, 0.9215686f); + TEMPLATE_COLORS[123] = NiColor(0.9254902f, 0.9176471f, 0.7450981f); + TEMPLATE_COLORS[124] = NiColor(1.0f, 0.8117647f, 0.282353f); + TEMPLATE_COLORS[125] = NiColor(0.9921569f, 0.3686275f, 0.3254902f); + TEMPLATE_COLORS[126] = NiColor(0.9803922f, 0.654902f, 0.4235294f); + TEMPLATE_COLORS[127] = NiColor(0.09411765f, 0.654902f, 0.7098039f); + TEMPLATE_COLORS[128] = NiColor(0.9215686f, 0.7803922f, 0.8745098f); + TEMPLATE_COLORS[129] = NiColor(0.9882353f, 0.5372549f, 0.6745098f); + TEMPLATE_COLORS[130] = NiColor(0.8588235f, 0.8431373f, 0.8235294f); + TEMPLATE_COLORS[131] = NiColor(0.8705882f, 0.6666667f, 0.5333334f); + TEMPLATE_COLORS[132] = NiColor(0.4666667f, 0.8666667f, 0.9058824f); + TEMPLATE_COLORS[133] = NiColor(1.0f, 1.0f, 0.4f); + TEMPLATE_COLORS[134] = NiColor(0.572549f, 0.4313726f, 0.682353f); + TEMPLATE_COLORS[135] = NiColor(0.1960784f, 0.2901961f, 0.6980392f); + TEMPLATE_COLORS[136] = NiColor(0.9686275f, 0.3254902f, 0.5803922f); + TEMPLATE_COLORS[137] = NiColor(1.0f, 0.627451f, 0.5372549f); + TEMPLATE_COLORS[138] = NiColor(0.5607843f, 0.3137255f, 0.6156863f); + TEMPLATE_COLORS[139] = NiColor(1.0f, 1.0f, 1.0f); + TEMPLATE_COLORS[140] = NiColor(0.6352941f, 0.6784314f, 0.8156863f); + TEMPLATE_COLORS[141] = NiColor(0.9882353f, 0.4235294f, 0.5215687f); + TEMPLATE_COLORS[142] = NiColor(0.8039216f, 0.6431373f, 0.8705882f); + TEMPLATE_COLORS[143] = NiColor(0.9882353f, 0.9098039f, 0.5137255f); + TEMPLATE_COLORS[144] = NiColor(0.772549f, 0.8901961f, 0.5176471f); + TEMPLATE_COLORS[145] = NiColor(1.0f, 0.682353f, 0.2588235f); + } + + const NiColor& Get(unsigned char index) const { + return (index < 146) ? TEMPLATE_COLORS[index] : FALLBACK_COLOR; + } + +private: + const NiColor FALLBACK_COLOR = NiColor(1.0f, 1.0f, 1.0f); + std::vector TEMPLATE_COLORS; +}; + +#endif // SCENE_COLOR_H diff --git a/dNavigation/CMakeLists.txt b/dNavigation/CMakeLists.txt index e2a1c6ef..2c776136 100644 --- a/dNavigation/CMakeLists.txt +++ b/dNavigation/CMakeLists.txt @@ -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 diff --git a/dNavigation/dNavMesh.cpp b/dNavigation/dNavMesh.cpp index d9584b00..d0432c89 100644 --- a/dNavigation/dNavMesh.cpp +++ b/dNavigation/dNavMesh.cpp @@ -1,6 +1,5 @@ #include "dNavMesh.h" -#include "RawFile.h" #include "Game.h" #include "Logger.h" diff --git a/dNavigation/dTerrain/CMakeLists.txt b/dNavigation/dTerrain/CMakeLists.txt deleted file mode 100644 index 91d0741b..00000000 --- a/dNavigation/dTerrain/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -set(DNAVIGATIONS_DTERRAIN_SOURCES "RawFile.cpp" - "RawChunk.cpp" - "RawHeightMap.cpp" PARENT_SCOPE) diff --git a/dNavigation/dTerrain/RawChunk.cpp b/dNavigation/dTerrain/RawChunk.cpp deleted file mode 100644 index fe7ac8a2..00000000 --- a/dNavigation/dTerrain/RawChunk.cpp +++ /dev/null @@ -1,92 +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 - - uint32_t colorMapSize; - BinaryIO::BinaryRead(stream, colorMapSize); - stream.seekg(static_cast(stream.tellg()) + (colorMapSize * colorMapSize * 4)); - - uint32_t lightmapSize; - BinaryIO::BinaryRead(stream, lightmapSize); - stream.seekg(static_cast(stream.tellg()) + (lightmapSize)); - - uint32_t colorMapSize2; - BinaryIO::BinaryRead(stream, colorMapSize2); - stream.seekg(static_cast(stream.tellg()) + (colorMapSize2 * colorMapSize2 * 4)); - - uint8_t unknown; - BinaryIO::BinaryRead(stream, unknown); - - uint32_t blendmapSize; - BinaryIO::BinaryRead(stream, blendmapSize); - stream.seekg(static_cast(stream.tellg()) + (blendmapSize)); - - uint32_t pointSize; - BinaryIO::BinaryRead(stream, pointSize); - stream.seekg(static_cast(stream.tellg()) + (pointSize * 9 * 4)); - - stream.seekg(static_cast(stream.tellg()) + (colorMapSize * colorMapSize)); - - uint32_t endCounter; - BinaryIO::BinaryRead(stream, endCounter); - stream.seekg(static_cast(stream.tellg()) + (endCounter * 2)); - - if (endCounter != 0) { - stream.seekg(static_cast(stream.tellg()) + (32)); - - for (int i = 0; i < 0x10; i++) { - uint16_t finalCountdown; - BinaryIO::BinaryRead(stream, finalCountdown); - stream.seekg(static_cast(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; -} diff --git a/dNavigation/dTerrain/RawChunk.h b/dNavigation/dTerrain/RawChunk.h deleted file mode 100644 index 1e26f11d..00000000 --- a/dNavigation/dTerrain/RawChunk.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include - -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; -}; diff --git a/dNavigation/dTerrain/RawFile.cpp b/dNavigation/dTerrain/RawFile.cpp deleted file mode 100644 index efc2b39d..00000000 --- a/dNavigation/dTerrain/RawFile.cpp +++ /dev/null @@ -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'; - } -} diff --git a/dNavigation/dTerrain/RawFile.h b/dNavigation/dTerrain/RawFile.h deleted file mode 100644 index b1fb4024..00000000 --- a/dNavigation/dTerrain/RawFile.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include -#include - -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 m_Chunks; - RawMesh* m_FinalMesh = nullptr; -}; diff --git a/dNavigation/dTerrain/RawHeightMap.cpp b/dNavigation/dTerrain/RawHeightMap.cpp deleted file mode 100644 index e1310669..00000000 --- a/dNavigation/dTerrain/RawHeightMap.cpp +++ /dev/null @@ -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() { - -} diff --git a/dNavigation/dTerrain/RawHeightMap.h b/dNavigation/dTerrain/RawHeightMap.h deleted file mode 100644 index 9a4bda3b..00000000 --- a/dNavigation/dTerrain/RawHeightMap.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include -#include - -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 m_FloatMap = {}; -}; diff --git a/dNavigation/dTerrain/RawMesh.h b/dNavigation/dTerrain/RawMesh.h deleted file mode 100644 index ed8eb4f3..00000000 --- a/dNavigation/dTerrain/RawMesh.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -#include "NiPoint3.h" - -struct RawMesh { - std::vector m_Vertices; - std::vector m_Triangles; -}; diff --git a/dZoneManager/CMakeLists.txt b/dZoneManager/CMakeLists.txt index e1ef9d28..ba6eff68 100644 --- a/dZoneManager/CMakeLists.txt +++ b/dZoneManager/CMakeLists.txt @@ -15,6 +15,7 @@ target_include_directories(dZoneManager PUBLIC "." "${PROJECT_SOURCE_DIR}/dGame" # Entity.h "${PROJECT_SOURCE_DIR}/dGame/dEntity" # EntityInfo.h PRIVATE + "${PROJECT_SOURCE_DIR}/dCommon/dClient" # SceneColors.h "${PROJECT_SOURCE_DIR}/dGame/dComponents" #InventoryComponent.h "${PROJECT_SOURCE_DIR}/dGame/dInventory" #InventoryComponent.h (transitive) "${PROJECT_SOURCE_DIR}/dGame/dBehaviors" #BehaviorSlot.h diff --git a/dZoneManager/Raw.cpp b/dZoneManager/Raw.cpp index ad02cf76..16fc9063 100644 --- a/dZoneManager/Raw.cpp +++ b/dZoneManager/Raw.cpp @@ -1,7 +1,9 @@ #include "Raw.h" #include "BinaryIO.h" #include "Logger.h" +#include "SceneColor.h" #include +#include namespace Raw { @@ -279,16 +281,22 @@ namespace Raw { return; // No scene data available } + LOG("GenerateTerrainMesh: Processing %u chunks", raw.chunks.size()); + uint32_t vertexOffset = 0; for (const auto& chunk : raw.chunks) { // Skip chunks without scene maps if (chunk.sceneMap.empty() || chunk.colorMapResolution == 0 || chunk.heightMap.empty()) { + LOG("Skipping chunk %u (sceneMap: %zu, colorMapRes: %u, heightMap: %zu)", + chunk.id, chunk.sceneMap.size(), chunk.colorMapResolution, chunk.heightMap.size()); continue; } + LOG("Processing chunk %u: width=%u, height=%u, colorMapRes=%u, sceneMapSize=%zu", + chunk.id, chunk.width, chunk.height, chunk.colorMapResolution, chunk.sceneMap.size()); + // Generate vertices for this chunk - // Similar to RawChunk::GenerateMesh() in dTerrain but with scene IDs for (uint32_t i = 0; i < chunk.width; ++i) { for (uint32_t j = 0; j < chunk.height; ++j) { // Get height at this position @@ -310,21 +318,23 @@ namespace Raw { NiPoint3 worldPos(worldX, worldY, worldZ); - // Get scene ID at this position - // Map heightmap position to scene map position - const float sceneMapX = (static_cast(i) / static_cast(chunk.width - 1)) * static_cast(chunk.colorMapResolution - 1); - const float sceneMapZ = (static_cast(j) / static_cast(chunk.height - 1)) * static_cast(chunk.colorMapResolution - 1); - - const uint32_t sceneX = std::min(static_cast(sceneMapX), chunk.colorMapResolution - 1); - const uint32_t sceneZ = std::min(static_cast(sceneMapZ), chunk.colorMapResolution - 1); - const uint32_t sceneIndex = sceneZ * chunk.colorMapResolution + sceneX; + // Get scene ID at this position + // Map heightmap position to scene map position + // The scene map is colorMapResolution x colorMapResolution + // We need to map from heightmap coordinates (i, j) to scene map coordinates + const float sceneMapI = (static_cast(i) / static_cast(chunk.width - 1)) * static_cast(chunk.colorMapResolution - 1); + const float sceneMapJ = (static_cast(j) / static_cast(chunk.height - 1)) * static_cast(chunk.colorMapResolution - 1); + + const uint32_t sceneI = std::min(static_cast(sceneMapI), chunk.colorMapResolution - 1); + const uint32_t sceneJ = std::min(static_cast(sceneMapJ), chunk.colorMapResolution - 1); + // Scene map uses the same indexing pattern as heightmap: row * width + col + const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ; - uint8_t sceneID = 0; - if (sceneIndex < chunk.sceneMap.size()) { - sceneID = chunk.sceneMap[sceneIndex]; - } - - outMesh.vertices.emplace_back(worldPos, sceneID); + uint8_t sceneID = 0; + if (sceneIndex < chunk.sceneMap.size()) { + sceneID = chunk.sceneMap[sceneIndex]; + } + outMesh.vertices.emplace_back(worldPos, sceneID); // Generate triangles (same pattern as dTerrain) if (i > 0 && j > 0) { @@ -346,8 +356,49 @@ namespace Raw { } } - vertexOffset += chunk.width * chunk.height; - } + vertexOffset += chunk.width * chunk.height; } +} -} // namespace Raw +bool WriteTerrainMeshToOBJ(const TerrainMesh& mesh, const std::string& path) { + try { + std::ofstream file(path); + if (!file.is_open()) { + LOG("Failed to open OBJ file for writing: %s", path.c_str()); + return false; + } + + // Create instance of SceneColor for color lookup + SceneColor sceneColor; + + // Write vertices with colors + // OBJ format supports vertex colors as: v x y z r g b + for (const auto& v : mesh.vertices) { + file << "v " << v.position.x << ' ' << v.position.y << ' ' << v.position.z; + + uint8_t sceneID = v.sceneID; + + const NiColor& color = sceneColor.Get(sceneID); + file << ' ' << color.m_Red << ' ' << color.m_Green << ' ' << color.m_Blue; + file << '\n'; + } + + // Write faces (triangles) + for (size_t i = 0; i < mesh.triangles.size(); i += 3) { + // OBJ indices are 1-based + file << "f " << (mesh.triangles[i] + 1) << ' ' + << (mesh.triangles[i + 1] + 1) << ' ' + << (mesh.triangles[i + 2] + 1) << '\n'; + } + + file.close(); + LOG("Successfully wrote terrain mesh to OBJ: %s (%zu vertices, %zu triangles)", + path.c_str(), mesh.vertices.size(), mesh.triangles.size() / 3); + return true; + } catch (const std::exception& e) { + LOG("Exception while writing OBJ file: %s", e.what()); + return false; + } +} + +} // namespace Raw \ No newline at end of file diff --git a/dZoneManager/Raw.h b/dZoneManager/Raw.h index 2409e987..fa65cded 100644 --- a/dZoneManager/Raw.h +++ b/dZoneManager/Raw.h @@ -7,7 +7,9 @@ #include #include #include +#include #include "NiPoint3.h" +#include "dCommonVars.h" namespace Raw { @@ -138,6 +140,17 @@ bool ReadRaw(std::istream& stream, Raw& outRaw); */ void GenerateTerrainMesh(const Raw& raw, TerrainMesh& outMesh); +/** + * @brief Write terrain mesh to OBJ file for debugging/visualization + * Merged from dTerrain's WriteFinalMeshToOBJ functionality + * Vertices are colored based on their scene ID using a hash function + * + * @param mesh The terrain mesh to export + * @param path Output path for the OBJ file + * @return true if successfully written, false otherwise + */ +bool WriteTerrainMeshToOBJ(const TerrainMesh& mesh, const std::string& path); + } // namespace Raw #endif // __RAW_H__ diff --git a/dZoneManager/Zone.cpp b/dZoneManager/Zone.cpp index 29245ff6..bc3a79e5 100644 --- a/dZoneManager/Zone.cpp +++ b/dZoneManager/Zone.cpp @@ -8,6 +8,7 @@ #include "GeneralUtils.h" #include "BinaryIO.h" #include "LUTriggers.h" +#include "dConfig.h" #include "AssetManager.h" #include "CDClientManager.h" @@ -102,6 +103,14 @@ void Zone::LoadZoneIntoMemory() { LOG("Generated terrain mesh with %llu vertices and %llu triangles", m_TerrainMesh.vertices.size(), m_TerrainMesh.triangles.size() / 3); + // Optionally export terrain mesh to OBJ for debugging/visualization + if (Game::config->GetValue("export_terrain_to_obj") == "1") { + std::string objFileName = "terrain_" + std::to_string(m_ZoneID.GetMapID()) + ".obj"; + if (Raw::WriteTerrainMeshToOBJ(m_TerrainMesh, objFileName)) { + LOG("Exported terrain mesh to %s", objFileName.c_str()); + } + } + if (m_FileFormatVersion >= Zone::FileFormatVersion::PreAlpha) { BinaryIO::BinaryRead(file, m_NumberOfSceneTransitionsLoaded); for (uint32_t i = 0; i < m_NumberOfSceneTransitionsLoaded; ++i) { diff --git a/dZoneManager/dZoneManager.cpp b/dZoneManager/dZoneManager.cpp index a757858e..5cb376d4 100644 --- a/dZoneManager/dZoneManager.cpp +++ b/dZoneManager/dZoneManager.cpp @@ -399,6 +399,23 @@ void dZoneManager::BuildSceneGraph() { } } } + + // Scene 0 (global scene) is always loaded and adjacent to all other scenes + LWOSCENEID globalScene = LWOSCENEID(m_ZoneID.GetMapID(), 0); + for (auto& [sceneID, adjacentScenes] : m_SceneAdjacencyList) { + if (sceneID != globalScene) { + // Add global scene to this scene's adjacency list if not already present + if (std::find(adjacentScenes.begin(), adjacentScenes.end(), globalScene) == adjacentScenes.end()) { + adjacentScenes.push_back(globalScene); + } + + // Add this scene to global scene's adjacency list if not already present + auto& globalAdjacent = m_SceneAdjacencyList[globalScene]; + if (std::find(globalAdjacent.begin(), globalAdjacent.end(), sceneID) == globalAdjacent.end()) { + globalAdjacent.push_back(sceneID); + } + } + } } std::vector dZoneManager::GetAdjacentScenes(LWOSCENEID sceneID) const { diff --git a/enc_temp_folder/48a84d7fa3b1c27696794fcc62d106d/Raw.cpp b/enc_temp_folder/48a84d7fa3b1c27696794fcc62d106d/Raw.cpp new file mode 100644 index 00000000..6276f35d --- /dev/null +++ b/enc_temp_folder/48a84d7fa3b1c27696794fcc62d106d/Raw.cpp @@ -0,0 +1,403 @@ +#include "Raw.h" +#include "BinaryIO.h" +#include "Logger.h" +#include "SceneColor.h" +#include +#include + +namespace Raw { + + /** + * @brief Read flair attributes from stream + */ + static bool ReadFlairAttributes(std::istream& stream, FlairAttributes& flair) { + try { + BinaryIO::BinaryRead(stream, flair.id); + BinaryIO::BinaryRead(stream, flair.scaleFactor); + BinaryIO::BinaryRead(stream, flair.position.x); + BinaryIO::BinaryRead(stream, flair.position.y); + BinaryIO::BinaryRead(stream, flair.position.z); + BinaryIO::BinaryRead(stream, flair.rotation.x); + BinaryIO::BinaryRead(stream, flair.rotation.y); + BinaryIO::BinaryRead(stream, flair.rotation.z); + BinaryIO::BinaryRead(stream, flair.colorR); + BinaryIO::BinaryRead(stream, flair.colorG); + BinaryIO::BinaryRead(stream, flair.colorB); + BinaryIO::BinaryRead(stream, flair.colorA); + return true; + } catch (const std::exception&) { + return false; + } + } + + /** + * @brief Read mesh triangle data from stream + */ + static bool ReadMeshTri(std::istream& stream, MeshTri& meshTri) { + try { + BinaryIO::BinaryRead(stream, meshTri.meshTriListSize); + meshTri.meshTriList.resize(meshTri.meshTriListSize); + for (uint16_t i = 0; i < meshTri.meshTriListSize; ++i) { + BinaryIO::BinaryRead(stream, meshTri.meshTriList[i]); + } + return true; + } catch (const std::exception&) { + return false; + } + } + + /** + * @brief Read a chunk from stream + */ + static bool ReadChunk(std::istream& stream, Chunk& chunk, uint16_t version) { + try { + // Read basic chunk info + BinaryIO::BinaryRead(stream, chunk.id); + if (stream.fail()) { + return false; + } + + BinaryIO::BinaryRead(stream, chunk.width); + BinaryIO::BinaryRead(stream, chunk.height); + BinaryIO::BinaryRead(stream, chunk.offsetWorldX); + BinaryIO::BinaryRead(stream, chunk.offsetWorldZ); + + if (stream.fail()) { + return false; + } + + // For version < 32, shader ID comes before texture IDs + if (version < 32) { + BinaryIO::BinaryRead(stream, chunk.shaderId); + } + + // Read texture IDs (4 textures) + chunk.textureIds.resize(4); + for (int i = 0; i < 4; ++i) { + BinaryIO::BinaryRead(stream, chunk.textureIds[i]); + } + + if (stream.fail()) { + return false; + } + + // Read scale factor + BinaryIO::BinaryRead(stream, chunk.scaleFactor); + + if (stream.fail()) { + return false; + } + + // Read heightmap + uint32_t heightMapSize = chunk.width * chunk.height; + + chunk.heightMap.resize(heightMapSize); + for (uint32_t i = 0; i < heightMapSize; ++i) { + BinaryIO::BinaryRead(stream, chunk.heightMap[i]); + } + + if (stream.fail()) { + return false; + } + + // ColorMap (size varies by version) + if (version >= 32) { + BinaryIO::BinaryRead(stream, chunk.colorMapResolution); + } else { + chunk.colorMapResolution = chunk.width; // Default to chunk width for older versions + } + + uint32_t colorMapPixelCount = chunk.colorMapResolution * chunk.colorMapResolution * 4; // RGBA + chunk.colorMap.resize(colorMapPixelCount); + stream.read(reinterpret_cast(chunk.colorMap.data()), colorMapPixelCount); + + if (stream.fail()) { + return false; + } + + // LightMap DDS + uint32_t lightMapSize; + BinaryIO::BinaryRead(stream, lightMapSize); + + chunk.lightMap.resize(lightMapSize); + stream.read(reinterpret_cast(chunk.lightMap.data()), lightMapSize); + + if (stream.fail()) { + return false; + } + + // TextureMap (size varies by version) + if (version >= 32) { + BinaryIO::BinaryRead(stream, chunk.textureMapResolution); + } else { + chunk.textureMapResolution = chunk.width; // Default to chunk width for older versions + } + + uint32_t textureMapPixelCount = chunk.textureMapResolution * chunk.textureMapResolution * 4; + chunk.textureMap.resize(textureMapPixelCount); + stream.read(reinterpret_cast(chunk.textureMap.data()), textureMapPixelCount); + + if (stream.fail()) { + return false; + } + + // Texture settings + BinaryIO::BinaryRead(stream, chunk.textureSettings); + + // Blend map DDS + uint32_t blendMapDDSSize; + BinaryIO::BinaryRead(stream, blendMapDDSSize); + + chunk.blendMap.resize(blendMapDDSSize); + stream.read(reinterpret_cast(chunk.blendMap.data()), blendMapDDSSize); + + if (stream.fail()) { + return false; + } + + // Read flairs + uint32_t numFlairs; + BinaryIO::BinaryRead(stream, numFlairs); + + if (stream.fail()) { + return false; + } + + chunk.flairs.resize(numFlairs); + for (uint32_t i = 0; i < numFlairs; ++i) { + if (!ReadFlairAttributes(stream, chunk.flairs[i])) { + return false; + } + } + + // Scene map (version 32+ only) + if (version >= 32) { + uint32_t sceneMapSize = chunk.colorMapResolution * chunk.colorMapResolution; + + chunk.sceneMap.resize(sceneMapSize); + stream.read(reinterpret_cast(chunk.sceneMap.data()), sceneMapSize); + + if (stream.fail()) { + return false; + } + } + + // Mesh vertex usage (read size first, then check if empty) + BinaryIO::BinaryRead(stream, chunk.vertSize); + + if (stream.fail()) { + return false; + } + + // Mesh vert usage + chunk.meshVertUsage.resize(chunk.vertSize); + for (uint32_t i = 0; i < chunk.vertSize; ++i) { + BinaryIO::BinaryRead(stream, chunk.meshVertUsage[i]); + } + + if (stream.fail()) { + return false; + } + + // Only continue with mesh data if we have vertex usage data + if (chunk.vertSize == 0) { + return true; + } + + // Mesh vert size (16 elements) + chunk.meshVertSize.resize(16); + for (int i = 0; i < 16; ++i) { + BinaryIO::BinaryRead(stream, chunk.meshVertSize[i]); + } + + if (stream.fail()) { + return false; + } + + // Mesh triangles (16 elements) + chunk.meshTri.resize(16); + for (int i = 0; i < 16; ++i) { + if (!ReadMeshTri(stream, chunk.meshTri[i])) { + return false; + } + } + + return true; + } catch (const std::exception&) { + return false; + } + } + + bool ReadRaw(std::istream& stream, Raw& outRaw) { + // Get stream size + stream.seekg(0, std::ios::end); + auto streamSize = stream.tellg(); + stream.seekg(0, std::ios::beg); + + if (streamSize <= 0) { + return false; + } + + try { + // Read header + BinaryIO::BinaryRead(stream, outRaw.version); + + if (stream.fail()) { + return false; + } + + BinaryIO::BinaryRead(stream, outRaw.dev); + + if (stream.fail()) { + return false; + } + + // Only read chunks if dev == 0 + if (outRaw.dev == 0) { + BinaryIO::BinaryRead(stream, outRaw.numChunks); + BinaryIO::BinaryRead(stream, outRaw.numChunksWidth); + BinaryIO::BinaryRead(stream, outRaw.numChunksHeight); + + // Read all chunks + outRaw.chunks.resize(outRaw.numChunks); + for (uint32_t i = 0; i < outRaw.numChunks; ++i) { + if (!ReadChunk(stream, outRaw.chunks[i], outRaw.version)) { + return false; + } + } + } + + return true; + } catch (const std::exception&) { + return false; + } + } + + void GenerateTerrainMesh(const Raw& raw, TerrainMesh& outMesh) { + outMesh.vertices.clear(); + outMesh.triangles.clear(); + + if (raw.chunks.empty() || raw.version < 32) { + return; // No scene data available + } + + LOG("GenerateTerrainMesh: Processing %u chunks", raw.chunks.size()); + + uint32_t vertexOffset = 0; + + for (const auto& chunk : raw.chunks) { + // Skip chunks without scene maps + if (chunk.sceneMap.empty() || chunk.colorMapResolution == 0 || chunk.heightMap.empty()) { + LOG("Skipping chunk %u (sceneMap: %zu, colorMapRes: %u, heightMap: %zu)", + chunk.id, chunk.sceneMap.size(), chunk.colorMapResolution, chunk.heightMap.size()); + continue; + } + + LOG("Processing chunk %u: width=%u, height=%u, colorMapRes=%u, sceneMapSize=%zu", + chunk.id, chunk.width, chunk.height, chunk.colorMapResolution, chunk.sceneMap.size()); + + // Generate vertices for this chunk + for (uint32_t i = 0; i < chunk.width; ++i) { + for (uint32_t j = 0; j < chunk.height; ++j) { + // Get height at this position + const uint32_t heightIndex = chunk.width * i + j; + if (heightIndex >= chunk.heightMap.size()) continue; + + const float y = chunk.heightMap[heightIndex]; + + // Calculate world position + // Based on RawFile::GenerateFinalMeshFromChunks in dTerrain: + // 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)); + // tempVert *= chunk->m_HeightMap->m_ScaleFactor; + + float worldX = (static_cast(i) + (chunk.offsetWorldX / chunk.scaleFactor)) * chunk.scaleFactor; + float worldY = (y / chunk.scaleFactor) * chunk.scaleFactor; + float worldZ = (static_cast(j) + (chunk.offsetWorldZ / chunk.scaleFactor)) * chunk.scaleFactor; + + NiPoint3 worldPos(worldX, worldY, worldZ); + + // Get scene ID at this position + // Map heightmap position to scene map position + // The scene map is colorMapResolution x colorMapResolution + // We need to map from heightmap coordinates (i, j) to scene map coordinates + const float sceneMapI = (static_cast(i) / static_cast(chunk.width - 1)) * static_cast(chunk.colorMapResolution - 1); + const float sceneMapJ = (static_cast(j) / static_cast(chunk.height - 1)) * static_cast(chunk.colorMapResolution - 1); + + const uint32_t sceneI = std::min(static_cast(sceneMapI), chunk.colorMapResolution - 1); + const uint32_t sceneJ = std::min(static_cast(sceneMapJ), chunk.colorMapResolution - 1); + // Scene map uses the same indexing pattern as heightmap: row * width + col + const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ; + + uint8_t sceneID = 0; + if (sceneIndex < chunk.sceneMap.size()) { + sceneID = chunk.sceneMap[sceneIndex]; + } outMesh.vertices.emplace_back(worldPos, sceneID); + + // Generate triangles (same pattern as dTerrain) + if (i > 0 && j > 0) { + const uint32_t currentVert = vertexOffset + chunk.width * i + j; + const uint32_t leftVert = currentVert - 1; + const uint32_t bottomLeftVert = vertexOffset + chunk.width * (i - 1) + j - 1; + const uint32_t bottomVert = vertexOffset + chunk.width * (i - 1) + j; + + // First triangle + outMesh.triangles.push_back(currentVert); + outMesh.triangles.push_back(leftVert); + outMesh.triangles.push_back(bottomLeftVert); + + // Second triangle + outMesh.triangles.push_back(bottomLeftVert); + outMesh.triangles.push_back(bottomVert); + outMesh.triangles.push_back(currentVert); + } + } + } + + vertexOffset += chunk.width * chunk.height; + } +} + +bool WriteTerrainMeshToOBJ(const TerrainMesh& mesh, const std::string& path) { + try { + std::ofstream file(path); + if (!file.is_open()) { + LOG("Failed to open OBJ file for writing: %s", path.c_str()); + return false; + } + + // Create instance of SceneColor for color lookup + SceneColor sceneColor; + + // Write vertices with colors + // OBJ format supports vertex colors as: v x y z r g b + for (const auto& v : mesh.vertices) { + file << "v " << v.position.x << ' ' << v.position.y << ' ' << v.position.z; + + uint8_t sceneID = v.sceneID; + + const NiColor& color = sceneColor.Get(sceneID); + file << ' ' << color.m_Red << ' ' << color.m_Green << ' ' << color.m_Blue; + file << '\n'; + } + + // Write faces (triangles) + for (size_t i = 0; i < mesh.triangles.size(); i += 3) { + // OBJ indices are 1-based + file << "f " << (mesh.triangles[i] + 1) << ' ' + << (mesh.triangles[i + 1] + 1) << ' ' + << (mesh.triangles[i + 2] + 1) << '\n'; + } + + file.close(); + LOG("Successfully wrote terrain mesh to OBJ: %s (%zu vertices, %zu triangles)", + path.c_str(), mesh.vertices.size(), mesh.triangles.size() / 3); + return true; + } catch (const std::exception& e) { + LOG("Exception while writing OBJ file: %s", e.what()); + return false; + } +} + +} // namespace Raw \ No newline at end of file diff --git a/resources/worldconfig.ini b/resources/worldconfig.ini index 38350086..a31b53d8 100644 --- a/resources/worldconfig.ini +++ b/resources/worldconfig.ini @@ -102,3 +102,7 @@ hardcore_disabled_worlds= # Keeps this percentage of a players' coins on death in hardcore hardcore_coin_keep= + +# Export terrain meshes to OBJ files when zones load +# OBJ files will be saved as terrain_.obj in the server directory +export_terrain_to_obj=0