11#include " MapTileRenderer.h"
22
3- #include < iostream>
4- #include < regex>
5-
63#include < glm/gtc/matrix_inverse.hpp>
74#include < spdlog/spdlog.h>
85
6+ #include " SatisfactorySave/GameTypes/Arrays/ObjectArray.h"
7+ #include " SatisfactorySave/GameTypes/Arrays/StructArray.h"
8+ #include " SatisfactorySave/GameTypes/Properties/ArrayProperty.h"
9+ #include " SatisfactorySave/GameTypes/Properties/NameProperty.h"
10+ #include " SatisfactorySave/GameTypes/Properties/ObjectProperty.h"
11+ #include " SatisfactorySave/GameTypes/Properties/StructProperty.h"
12+ #include " SatisfactorySave/GameTypes/Structs/PropertyStruct.h"
913#include " SatisfactorySave/GameTypes/UE/Engine/Engine/StaticMesh.h"
1014#include " SatisfactorySave/GameTypes/UE/Engine/Engine/Texture2D.h"
15+ #include " SatisfactorySave/GameTypes/UE/Engine/GameFramework/Actor.h"
1116
12- #include " ../OpenGL/Texture .h"
17+ #include " ../OpenGL/GlowlFactory .h"
1318#include " Utils/ResourceUtils.h"
1419
15- namespace {
16- #if 0 // TODO update for new game version
17- const SatisfactorySave::ObjectExport& getExportByClass(const SatisfactorySave::AssetFile& asset,
18- const std::string& class_name) {
19- // Validate asset has exactly one "class_name" export
20- int exportIdx = -1;
21- for (int i = 0; i < asset.exportMap().size(); i++) {
22- const auto& exportEntry = asset.exportMap()[i];
23- if (exportEntry.ClassIndex >= 0) {
24- throw std::runtime_error("exportEntry.ClassIndex >= 0 not implemented!");
25- }
26- const auto& importEntry = asset.importMap().at(-exportEntry.ClassIndex - 1);
27- if (importEntry.ObjectName == class_name) {
28- if (exportIdx >= 0) {
29- throw std::runtime_error("Found more than one " + class_name + " in asset!");
30- }
31- exportIdx = i;
32- }
33- }
34- if (exportIdx < 0) {
35- throw std::runtime_error("No " + class_name + " found in asset!");
36- }
37- return asset.exportMap()[exportIdx];
38- }
39- #endif
40- } // namespace
41-
4220Satisfactory3DMap::MapTileRenderer::MapTileRenderer (const std::shared_ptr<Configuration>& config,
4321 const std::shared_ptr<SatisfactorySave::PakManager>& pakManager) {
4422
@@ -48,112 +26,102 @@ Satisfactory3DMap::MapTileRenderer::MapTileRenderer(const std::shared_ptr<Config
4826 faceNormalsSetting_ = BoolSetting::create (" Use face normals" , false );
4927 config->registerSetting (faceNormalsSetting_);
5028
51- const std::vector<float > pos_x = {
52- -254000 .0f ,
53- -152400 .0f ,
54- -50800 .0f ,
55- 50800 .0f ,
56- 152400 .0f ,
57- 254000 .0f ,
58- 355600 .0f ,
59- };
60- const std::vector<float > pos_y = {
61- 254000 .0f ,
62- 152400 .0f ,
63- 50800 .0f ,
64- -50800 .0f ,
65- -152400 .0f ,
66- -254000 .0f ,
67- };
68-
69- #if 0 // TODO update for new game version
7029 if (pakManager != nullptr ) {
7130 try {
31+ if (!pakManager->containsAssetFilename (" Game/FactoryGame/Map/GameLevel01/Persistent_Level.umap" )) {
32+ throw std::runtime_error (" Missing Persistent_Level asset!" );
33+ }
34+ auto mapAsset = pakManager->readAsset (" Game/FactoryGame/Map/GameLevel01/Persistent_Level.umap" );
7235
73- const std::regex regex(
74- "Game/FactoryGame/Map/GameLevel01/Tile_X([0-9]+)_Y([0-9]+)LOD/SM_(Landscape|PROXY_Tile).*\\.uasset");
75- std::smatch match;
36+ auto WPHLODClassIndex = pakManager->tryGetScriptObjectIndex (" /Script/Engine/WorldPartitionHLOD" );
37+ if (!WPHLODClassIndex.has_value ()) {
38+ throw std::runtime_error (" ClassIndex missing for /Script/Engine/WorldPartitionHLOD!" );
39+ }
7640
77- for (const auto& filename : pakManager->getAllAssetFilenames()) {
78- if (std::regex_match(filename, match, regex)) {
79- if (match.size() != 4) {
80- throw std::runtime_error("Filename regex error!");
81- }
82- int tileX = std::stoi(match[1].str());
83- int tileY = std::stoi(match[2].str());
84- bool isLandscape = match[3].str() == "Landscape";
85- bool isNewPosFormat = tileX > 4 || (tileX > 1 && tileY > 3); // Changed tiles with Update 6.
86-
87- SatisfactorySave::AssetFile asset = pakManager->readAsset(filename);
88-
89- const auto& staticMeshExportEntry = getExportByClass(asset, "StaticMesh");
90-
91- // Serialize StaticMesh
92- asset.seek(staticMeshExportEntry.SerialOffset);
93- SatisfactorySave::UStaticMesh staticMesh;
94- asset << staticMesh;
95-
96- // Textures
97- std::string texname = std::regex_replace(filename, std::regex("SM_"), "T_");
98- std::string texname_d;
99- std::string texname_n;
100- if (isLandscape) {
101- texname_d = std::regex_replace(texname, std::regex(".uasset"), "_D.uasset");
102- texname_n = std::regex_replace(texname, std::regex(".uasset"), "_N.uasset");
103- } else {
104- texname_d = std::regex_replace(texname, std::regex(".uasset"), "_Diffuse.uasset");
105- texname_n = std::regex_replace(texname, std::regex(".uasset"), "_Normal.uasset");
106- }
107- if (!pakManager->containsAssetFilename(texname_d) ||
108- !pakManager->containsAssetFilename(texname_n)) {
109- throw std::runtime_error("Texture not found!");
110- }
41+ for (std::size_t i = 0 ; i < mapAsset.exportMap ().size (); i++) {
42+ const auto & exp = mapAsset.exportMap ()[i];
43+ if (exp.ClassIndex != WPHLODClassIndex.value ()) {
44+ continue ;
45+ }
11146
112- // Diffuse texture
113- SatisfactorySave::AssetFile assetTexD = pakManager->readAsset(texname_d);
114- const auto& texDExportEntry = getExportByClass(assetTexD, "Texture2D");
47+ const auto WPHLOD = mapAsset.getExportObjectByIdx (i);
48+ const auto WPHLOD_obj = WPHLOD->cast_object <s::AActor>();
11549
116- assetTexD.seek(texDExportEntry.SerialOffset);
117- SatisfactorySave::UTexture2D texD;
118- assetTexD << texD;
50+ // TODO WPHLOD_obj->ActorLabel;
11951
120- // Normal texture
121- SatisfactorySave::AssetFile assetTexN = pakManager->readAsset(texname_n);
122- const auto& texNExportEntry = getExportByClass(assetTexN, "Texture2D");
52+ const auto & instCompProp = WPHLOD_obj->Properties .get <s::ArrayProperty>(" InstanceComponents" );
53+ const auto instCompArray = std::dynamic_pointer_cast<s::ObjectArray>(instCompProp.Value );
54+ if (instCompArray == nullptr ) {
55+ throw std::runtime_error (" InstanceComponents array missing!" );
56+ }
12357
124- assetTexN.seek(texNExportEntry.SerialOffset);
125- SatisfactorySave::UTexture2D texN;
126- assetTexN << texN;
58+ for (const auto & instCompRef : instCompArray->Values ) {
59+ if (instCompRef.pakValue () <= 0 ) {
60+ continue ;
61+ }
12762
128- // Render data
129- MapTileData mapTile;
130- mapTile.mesh = makeGlowlMesh(staticMesh);
131- mapTile.texD = makeOpenGLTexture(texD);
132- mapTile.texN = makeOpenGLTexture(texN);
133- mapTile.tileX = tileX;
134- mapTile.tileY = tileY;
135-
136- // Precalculate matrices
137- float x = 0.0f;
138- float y = 0.0f;
139- if (isLandscape || !isNewPosFormat) {
140- x = pos_x[tileX];
141- y = pos_y[tileY];
63+ const auto instCompExp = mapAsset.getExportObjectByIdx (instCompRef.pakValue () - 1 );
64+ const auto & meshRef = instCompExp->Object ->Properties .get <s::ObjectProperty>(" StaticMesh" ).Value ;
65+ if (meshRef.pakValue () <= 0 ) {
66+ continue ;
67+ }
68+ const auto meshExp = mapAsset.getExportObjectByIdx (meshRef.pakValue () - 1 );
69+ const auto meshObj = meshExp->cast_object <s::UStaticMesh>();
70+ if (meshObj == nullptr ) {
71+ throw std::runtime_error (" Invalid UStaticMesh!" );
14272 }
14373
144- const float offset = isLandscape ? -50800.0f : 0.0f;
74+ MapTileData mapTile;
75+ mapTile.mesh = makeGlowlMesh (*meshObj);
76+
77+ mapTile.modelMx = glm::scale (glm::mat4 (1 .0f ), glm::vec3 (0 .01f ));
78+ mapTile.normalMx = glm::inverseTranspose (glm::mat3 (mapTile.modelMx ));
14579
146- glm::vec3 position_((x + offset) * 0.01f, -(y + offset) * 0.01f, 0.0f);
147- glm::vec4 rotation_(0.0f, 0.0f, 0.0f, 1.0f);
148- glm::vec3 scale_(0.01f);
80+ const auto & matProp = meshObj->Properties .get <s::ArrayProperty>(" StaticMaterials" );
81+ const auto matArray = std::dynamic_pointer_cast<s::StructArray>(matProp.Value );
82+ if (matArray == nullptr || matArray->Values .size () != 1 ) {
83+ throw std::runtime_error (" Invalid/Unexpected StaticMaterials array!" );
84+ }
85+ const auto matStruct = std::dynamic_pointer_cast<s::PropertyStruct>(matArray->Values [0 ]);
86+ if (matStruct == nullptr ) {
87+ throw std::runtime_error (" Invalid StaticMaterials PropertyStruct!" );
88+ }
89+ const auto & matInterfaceRef = matStruct->Data .get <s::ObjectProperty>(" MaterialInterface" ).Value ;
90+ if (matInterfaceRef.pakValue () <= 0 ) {
91+ throw std::runtime_error (" Invalid MaterialInterface reference!" );
92+ }
14993
150- const auto translation = glm::translate(glm::mat4(1.0f), position_);
151- const auto rotation =
152- glm::mat4_cast(glm::quat(-rotation_.w, rotation_.x, -rotation_.y, rotation_.z));
153- const auto scale = glm::scale(glm::mat4(1.0f), scale_);
94+ const auto matExp = mapAsset.getExportObjectByIdx (matInterfaceRef.pakValue () - 1 );
95+ const auto & texParamProp =
96+ matExp->Object ->Properties .get <s::ArrayProperty>(" TextureParameterValues" );
97+ const auto texParamArray = std::dynamic_pointer_cast<s::StructArray>(texParamProp.Value );
98+ if (texParamArray == nullptr || texParamArray->Values .size () != 3 ) {
99+ throw std::runtime_error (" Invalid TextureParameterValues array!" );
100+ }
154101
155- mapTile.modelMx = translation * rotation * scale;
156- mapTile.normalMx = glm::inverseTranspose(glm::mat3(mapTile.modelMx));
102+ const auto texParam0Struct = std::dynamic_pointer_cast<s::PropertyStruct>(texParamArray->Values [0 ]);
103+ const auto & texParam0Info = texParam0Struct->Data .get <s::StructProperty>(" ParameterInfo" );
104+ const auto texParam0InfoStruct = std::dynamic_pointer_cast<s::PropertyStruct>(texParam0Info.Value );
105+ const auto name0 = texParam0InfoStruct->Data .get <s::NameProperty>(" Name" ).Value .toString ();
106+ if (name0 != " BaseColorTexture" ) {
107+ throw std::runtime_error (" Expected BaseColorTexture!" );
108+ }
109+ const auto & texParam0ValueRef =
110+ texParam0Struct->Data .get <s::ObjectProperty>(" ParameterValue" ).Value ;
111+ const auto tex0Exp = mapAsset.getExportObjectByIdx (texParam0ValueRef.pakValue () - 1 );
112+ mapTile.texBaseColor = std::make_unique<OGLTexture2D>(*tex0Exp->cast_object <s::UTexture2D>());
113+
114+ const auto texParam1Struct = std::dynamic_pointer_cast<s::PropertyStruct>(texParamArray->Values [1 ]);
115+ const auto & texParam1Info = texParam1Struct->Data .get <s::StructProperty>(" ParameterInfo" );
116+ const auto texParam1InfoStruct = std::dynamic_pointer_cast<s::PropertyStruct>(texParam1Info.Value );
117+ const auto name1 = texParam1InfoStruct->Data .get <s::NameProperty>(" Name" ).Value .toString ();
118+ if (name1 != " NormalTexture" ) {
119+ throw std::runtime_error (" Expected NormalTexture!" );
120+ }
121+ const auto & texParam1ValueRef =
122+ texParam1Struct->Data .get <s::ObjectProperty>(" ParameterValue" ).Value ;
123+ const auto tex1Exp = mapAsset.getExportObjectByIdx (texParam1ValueRef.pakValue () - 1 );
124+ mapTile.texNormal = std::make_unique<OGLTexture2D>(*tex1Exp->cast_object <s::UTexture2D>());
157125
158126 mapTiles_.push_back (std::move (mapTile));
159127 }
@@ -162,7 +130,6 @@ Satisfactory3DMap::MapTileRenderer::MapTileRenderer(const std::shared_ptr<Config
162130 spdlog::warn (" Error loading Pak file: {}" , ex.what ());
163131 }
164132 }
165- #endif
166133
167134 try {
168135 meshShader_ = std::make_unique<glowl::GLSLProgram>(glowl::GLSLProgram::ShaderSourceList{
@@ -196,12 +163,12 @@ void Satisfactory3DMap::MapTileRenderer::render(const glm::mat4& projMx, const g
196163 shader->setUniform (" normalMx" , tile.normalMx );
197164
198165 glActiveTexture (GL_TEXTURE0);
199- glBindTexture (GL_TEXTURE_2D, tile.texD );
200- shader->setUniform (" texD " , 0 );
166+ tile.texBaseColor -> bindTexture ( );
167+ shader->setUniform (" texBaseColor " , 0 );
201168
202169 glActiveTexture (GL_TEXTURE1);
203- glBindTexture (GL_TEXTURE_2D, tile.texN );
204- shader->setUniform (" texN " , 1 );
170+ tile.texNormal -> bindTexture ( );
171+ shader->setUniform (" texNormal " , 1 );
205172
206173 tile.mesh ->draw ();
207174 }
0 commit comments