mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-22 00:09:18 +02:00
EU7 scenery format and models streaming
This commit is contained in:
194
scene/eu7/eu7_apply.cpp
Normal file
194
scene/eu7/eu7_apply.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "scene/eu7/eu7_apply.h"
|
||||
|
||||
#include "scene/eu7/eu7_loader.h"
|
||||
#include "scene/eu7/eu7_load_stats.h"
|
||||
#include "scene/eu7/eu7_transform.h"
|
||||
#include "simulation/simulation.h"
|
||||
#include "simulation/simulationstateserializer.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
namespace {
|
||||
|
||||
void
|
||||
insert_mesh_shapes(
|
||||
basic_region ®ion,
|
||||
Eu7Module const &module,
|
||||
scene::scratch_data &scratch ) {
|
||||
for( auto const &source : module.scene.shapes ) {
|
||||
shape_node shape { build_shape_node( source ) };
|
||||
region.insert( shape, scratch, true );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
insert_line_shapes(
|
||||
basic_region ®ion,
|
||||
Eu7Module const &module,
|
||||
scene::scratch_data &scratch ) {
|
||||
for( auto const &source : module.scene.lines ) {
|
||||
lines_node lines { build_lines_node( source ) };
|
||||
region.insert( lines, scratch );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
transform_segment_path( Eu7SegmentPath &path, Eu7TransformContext const &prefix ) {
|
||||
path.p_start = transform_point( path.p_start, prefix );
|
||||
path.cp_out = transform_vector( path.cp_out, prefix );
|
||||
path.cp_in = transform_vector( path.cp_in, prefix );
|
||||
path.p_end = transform_point( path.p_end, prefix );
|
||||
}
|
||||
|
||||
void
|
||||
transform_shape_vertices( std::vector<Eu7WorldVertex> &vertices, Eu7TransformContext const &prefix ) {
|
||||
for( auto &vertex : vertices ) {
|
||||
vertex.position = transform_point( vertex.position, prefix );
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool
|
||||
is_model_only_module( Eu7Module const &module ) {
|
||||
if( module.has_terrain_chunk ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const &scene { module.scene };
|
||||
return (
|
||||
false == scene.models.empty() &&
|
||||
scene.tracks.empty() &&
|
||||
scene.traction.empty() &&
|
||||
scene.power_sources.empty() &&
|
||||
scene.shapes.empty() &&
|
||||
scene.terrain_shapes.empty() &&
|
||||
scene.lines.empty() &&
|
||||
scene.memcells.empty() &&
|
||||
scene.event_launchers.empty() &&
|
||||
scene.dynamics.empty() &&
|
||||
scene.sounds.empty() &&
|
||||
scene.trainsets.empty() &&
|
||||
scene.events.empty() &&
|
||||
scene.first_init_count == 0 );
|
||||
}
|
||||
|
||||
void
|
||||
compose_models_with_prefix( std::vector<Eu7Model> &models, Eu7TransformContext const &prefix ) {
|
||||
if( transform_is_empty( prefix ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for( auto &model : models ) {
|
||||
model.location = transform_point( model.location, prefix );
|
||||
model.angles.x += prefix.rotation.x;
|
||||
model.angles.y += prefix.rotation.y;
|
||||
model.angles.z += prefix.rotation.z;
|
||||
compose_node_transform( model.node.transform, prefix );
|
||||
model.node.area.center = model.location;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
compose_scene_with_include_prefix( Eu7Scene &scene, Eu7TransformContext const &prefix ) {
|
||||
if( transform_is_empty( prefix ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for( auto &track : scene.tracks ) {
|
||||
for( auto &path : track.paths ) {
|
||||
transform_segment_path( path, prefix );
|
||||
}
|
||||
compose_node_transform( track.node.transform, prefix );
|
||||
}
|
||||
|
||||
for( auto &traction : scene.traction ) {
|
||||
traction.wire_p1 = transform_point( traction.wire_p1, prefix );
|
||||
traction.wire_p2 = transform_point( traction.wire_p2, prefix );
|
||||
traction.wire_p3 = transform_point( traction.wire_p3, prefix );
|
||||
traction.wire_p4 = transform_point( traction.wire_p4, prefix );
|
||||
compose_node_transform( traction.node.transform, prefix );
|
||||
}
|
||||
|
||||
for( auto &source : scene.power_sources ) {
|
||||
source.position = transform_point( source.position, prefix );
|
||||
compose_node_transform( source.node.transform, prefix );
|
||||
}
|
||||
|
||||
for( auto &shape : scene.shapes ) {
|
||||
shape.origin = transform_point( shape.origin, prefix );
|
||||
transform_shape_vertices( shape.vertices, prefix );
|
||||
compose_node_transform( shape.node.transform, prefix );
|
||||
}
|
||||
|
||||
for( auto &shape : scene.terrain_shapes ) {
|
||||
shape.origin = transform_point( shape.origin, prefix );
|
||||
transform_shape_vertices( shape.vertices, prefix );
|
||||
compose_node_transform( shape.node.transform, prefix );
|
||||
}
|
||||
|
||||
for( auto &lines : scene.lines ) {
|
||||
lines.origin = transform_point( lines.origin, prefix );
|
||||
transform_shape_vertices( lines.vertices, prefix );
|
||||
compose_node_transform( lines.node.transform, prefix );
|
||||
}
|
||||
|
||||
compose_models_with_prefix( scene.models, prefix );
|
||||
|
||||
for( auto &cell : scene.memcells ) {
|
||||
cell.node.area.center = transform_point( cell.node.area.center, prefix );
|
||||
compose_node_transform( cell.node.transform, prefix );
|
||||
}
|
||||
|
||||
for( auto &launcher : scene.event_launchers ) {
|
||||
launcher.location = transform_point( launcher.location, prefix );
|
||||
compose_node_transform( launcher.node.transform, prefix );
|
||||
launcher.node.area.center = launcher.location;
|
||||
}
|
||||
|
||||
for( auto &vehicle : scene.dynamics ) {
|
||||
compose_node_transform( vehicle.node.transform, prefix );
|
||||
}
|
||||
|
||||
for( auto &sound : scene.sounds ) {
|
||||
sound.location = transform_point( sound.location, prefix );
|
||||
compose_node_transform( sound.node.transform, prefix );
|
||||
sound.node.area.center = sound.location;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
apply_module(
|
||||
Eu7Module const &module,
|
||||
simulation::state_serializer &serializer,
|
||||
scene::scratch_data &scratch ) {
|
||||
if( simulation::Region != nullptr ) {
|
||||
{
|
||||
ScopedTimer const timer { load_stats().terr_ms };
|
||||
insert_terrain_shapes( *simulation::Region, module );
|
||||
}
|
||||
{
|
||||
ScopedTimer const timer { load_stats().mesh_ms };
|
||||
insert_mesh_shapes( *simulation::Region, module, scratch );
|
||||
}
|
||||
{
|
||||
ScopedTimer const timer { load_stats().line_ms };
|
||||
insert_line_shapes( *simulation::Region, module, scratch );
|
||||
}
|
||||
}
|
||||
|
||||
serializer.apply_eu7_scene( module.scene, scratch );
|
||||
}
|
||||
|
||||
} // namespace scene::eu7
|
||||
37
scene/eu7/eu7_apply.h
Normal file
37
scene/eu7/eu7_apply.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/eu7/eu7_types.h"
|
||||
|
||||
namespace scene {
|
||||
class scratch_data;
|
||||
}
|
||||
|
||||
namespace simulation {
|
||||
class state_serializer;
|
||||
}
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
[[nodiscard]] bool
|
||||
is_model_only_module( Eu7Module const &module );
|
||||
|
||||
void
|
||||
compose_models_with_prefix( std::vector<Eu7Model> &models, Eu7TransformContext const &prefix );
|
||||
|
||||
// Wczytuje zawartosc Eu7Module do symulacji (bez ponownego INCL i bez petli include).
|
||||
void
|
||||
apply_module(
|
||||
Eu7Module const &Module,
|
||||
simulation::state_serializer &Serializer,
|
||||
scene::scratch_data &Scratch );
|
||||
|
||||
} // namespace scene::eu7
|
||||
337
scene/eu7/eu7_bake.cpp
Normal file
337
scene/eu7/eu7_bake.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
|
||||
This Source Code Form is subject to the
|
||||
|
||||
terms of the Mozilla Public License, v.
|
||||
|
||||
2.0. If a copy of the MPL was not
|
||||
|
||||
distributed with this file, You can
|
||||
|
||||
obtain one at
|
||||
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "scene/eu7/eu7_bake.h"
|
||||
|
||||
|
||||
|
||||
#include "scene/eu7/eu7_loader.h"
|
||||
|
||||
#include "utilities/Globals.h"
|
||||
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
#include "utilities/utilities.h"
|
||||
|
||||
|
||||
|
||||
#include <thread>
|
||||
|
||||
|
||||
|
||||
#ifdef WITH_EU7_PARSER
|
||||
|
||||
#include "scene/eu7/eu7_bake_parser.h"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] unsigned
|
||||
|
||||
bake_thread_count() {
|
||||
|
||||
if( Global.eu7_bake_threads > 0 ) {
|
||||
|
||||
return static_cast<unsigned>( Global.eu7_bake_threads );
|
||||
|
||||
}
|
||||
|
||||
auto const hardware { std::thread::hardware_concurrency() };
|
||||
|
||||
return hardware > 0 ? hardware : 1u;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef WITH_EU7_PARSER
|
||||
|
||||
[[nodiscard]] bool
|
||||
|
||||
run_scenario_tree_bake(
|
||||
|
||||
std::string const &text_root,
|
||||
|
||||
std::string const &eu7_path,
|
||||
|
||||
Eu7BakeOutcome &outcome ) {
|
||||
|
||||
WriteLog(
|
||||
|
||||
"EU7 bake: generowanie modulow z \"" + text_root + "\" (watek=" +
|
||||
|
||||
std::to_string( bake_thread_count() ) + ")" );
|
||||
|
||||
|
||||
|
||||
std::string error;
|
||||
|
||||
if( false == bake_parser::bake_scenario_tree( text_root, bake_thread_count(), error ) ) {
|
||||
|
||||
if( probe_file( eu7_path ) ) {
|
||||
|
||||
ErrorLog(
|
||||
|
||||
"EU7 bake nieudany, uzywam istniejacego modulu: " + error );
|
||||
|
||||
outcome.ok = true;
|
||||
|
||||
outcome.message = "fallback na stary .eu7 po bledzie bake";
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
outcome.message = error;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if( false == probe_file( eu7_path ) ) {
|
||||
|
||||
outcome.message = "bake zakonczony, ale brak pliku: " + eu7_path;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
outcome.ok = true;
|
||||
|
||||
outcome.regenerated = true;
|
||||
|
||||
outcome.message = eu7_path;
|
||||
|
||||
WriteLog( "EU7 bake: zapisano " + eu7_path );
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
||||
bool
|
||||
|
||||
scenario_needs_eu7_regen( std::string const &scenario_file ) {
|
||||
|
||||
#ifdef WITH_EU7_PARSER
|
||||
|
||||
if( false == Global.eu7_auto_bake ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
auto const resolved { resolve_scenery_path( scenario_file ) };
|
||||
|
||||
|
||||
|
||||
if( probe_file( resolved ) ) {
|
||||
|
||||
return text_module_is_newer_than_binary( scenario_file );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if( false == is_text_module_extension( resolved ) || false == FileExists( resolved ) ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return false == should_use_binary_module( scenario_file );
|
||||
|
||||
#else
|
||||
|
||||
(void)scenario_file;
|
||||
|
||||
return false;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Eu7BakeOutcome
|
||||
|
||||
ensure_scenario_eu7( std::string const &scenario_file ) {
|
||||
|
||||
Eu7BakeOutcome outcome;
|
||||
|
||||
auto const resolved { resolve_scenery_path( scenario_file ) };
|
||||
|
||||
auto const eu7_path { resolve_scenery_path( binary_path( scenario_file ) ) };
|
||||
|
||||
|
||||
|
||||
if( probe_file( resolved ) ) {
|
||||
|
||||
#ifdef WITH_EU7_PARSER
|
||||
|
||||
if( Global.eu7_auto_bake && text_module_is_newer_than_binary( scenario_file ) ) {
|
||||
|
||||
auto const text_root { text_source_path( scenario_file ) };
|
||||
|
||||
if( false == text_root.empty() ) {
|
||||
|
||||
WriteLog(
|
||||
|
||||
"EU7: .eu7 nieaktualny wzgledem " + text_root + ", rebake..." );
|
||||
|
||||
run_scenario_tree_bake( text_root, eu7_path, outcome );
|
||||
|
||||
return outcome;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
outcome.ok = true;
|
||||
|
||||
outcome.message = "uzywam .eu7: " + resolved;
|
||||
|
||||
WriteLog( "EU7: uzywam .eu7: " + resolved );
|
||||
|
||||
return outcome;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef WITH_EU7_PARSER
|
||||
|
||||
if( false == Global.eu7_auto_bake ) {
|
||||
|
||||
if( should_use_binary_module( scenario_file ) ) {
|
||||
|
||||
outcome.ok = true;
|
||||
|
||||
outcome.message = "eu7_auto_bake wylaczone, uzywam istniejacego .eu7";
|
||||
|
||||
return outcome;
|
||||
|
||||
}
|
||||
|
||||
outcome.ok = true;
|
||||
|
||||
outcome.message = "eu7_auto_bake wylaczone, ladowanie SCM";
|
||||
|
||||
return outcome;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if( false == is_text_module_extension( resolved ) ) {
|
||||
|
||||
outcome.ok = true;
|
||||
|
||||
outcome.message = "nie jest scenariuszem tekstowym";
|
||||
|
||||
return outcome;
|
||||
|
||||
}
|
||||
|
||||
if( false == FileExists( resolved ) ) {
|
||||
|
||||
outcome.message = "brak pliku scenariusza: " + resolved;
|
||||
|
||||
return outcome;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if( should_use_binary_module( scenario_file ) ) {
|
||||
|
||||
outcome.ok = true;
|
||||
|
||||
outcome.message = "uzywam istniejacego .eu7: " + eu7_path;
|
||||
|
||||
WriteLog( "EU7: uzywam .eu7: " + eu7_path );
|
||||
|
||||
return outcome;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if( probe_file( eu7_path ) ) {
|
||||
|
||||
WriteLog(
|
||||
|
||||
"EU7: .eu7 nieaktualny wzgledem " + resolved + ", rebake..." );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
run_scenario_tree_bake( resolved, eu7_path, outcome );
|
||||
|
||||
return outcome;
|
||||
|
||||
#else
|
||||
|
||||
if( should_use_binary_module( scenario_file ) ) {
|
||||
|
||||
outcome.ok = true;
|
||||
|
||||
outcome.message = "uzywam istniejacego .eu7 (parser niedostepny w buildzie)";
|
||||
|
||||
return outcome;
|
||||
|
||||
}
|
||||
|
||||
outcome.ok = true;
|
||||
|
||||
outcome.message = "parser niedostepny, ladowanie SCM";
|
||||
|
||||
return outcome;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace scene::eu7
|
||||
30
scene/eu7/eu7_bake.h
Normal file
30
scene/eu7/eu7_bake.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
struct Eu7BakeOutcome {
|
||||
bool ok { false };
|
||||
bool regenerated { false };
|
||||
std::string message;
|
||||
};
|
||||
|
||||
// Czy wejscie to tekst SCM/SCN bez gotowego .eu7 przy wlaczonym auto-bake.
|
||||
[[nodiscard]] bool
|
||||
scenario_needs_eu7_regen( std::string const &ScenarioFile );
|
||||
|
||||
// Przed ladowaniem mapy: uzyj istniejacego <stem>.eu7 lub wygeneruj cale drzewo include.
|
||||
[[nodiscard]] Eu7BakeOutcome
|
||||
ensure_scenario_eu7( std::string const &ScenarioFile );
|
||||
|
||||
} // namespace scene::eu7
|
||||
170
scene/eu7/eu7_bake_parser.cpp
Normal file
170
scene/eu7/eu7_bake_parser.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
|
||||
This Source Code Form is subject to the
|
||||
|
||||
terms of the Mozilla Public License, v.
|
||||
|
||||
2.0. If a copy of the MPL was not
|
||||
|
||||
distributed with this file, You can
|
||||
|
||||
obtain one at
|
||||
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <eu07/scene/bake/bake_tree.hpp>
|
||||
|
||||
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
|
||||
namespace scene::eu7::bake_parser {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
|
||||
std::mutex g_progress_log_mutex;
|
||||
|
||||
|
||||
|
||||
void
|
||||
|
||||
log_bake_progress( eu07::scene::bake::BakeProgress const &progress ) {
|
||||
|
||||
std::lock_guard<std::mutex> lock { g_progress_log_mutex };
|
||||
|
||||
auto const path { progress.path.u8string() };
|
||||
|
||||
switch( progress.phase ) {
|
||||
|
||||
case eu07::scene::bake::BakeProgressPhase::Starting:
|
||||
|
||||
case eu07::scene::bake::BakeProgressPhase::Start:
|
||||
|
||||
break;
|
||||
|
||||
case eu07::scene::bake::BakeProgressPhase::PackModels:
|
||||
|
||||
case eu07::scene::bake::BakeProgressPhase::PackCompose:
|
||||
|
||||
case eu07::scene::bake::BakeProgressPhase::PackComposeDone:
|
||||
|
||||
case eu07::scene::bake::BakeProgressPhase::Bake:
|
||||
|
||||
case eu07::scene::bake::BakeProgressPhase::Done:
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
(void)path;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] bool
|
||||
|
||||
is_text_scenery_path( fs::path const &path ) {
|
||||
|
||||
auto const ext { path.extension().string() };
|
||||
|
||||
return ext == ".scn" || ext == ".scm" || ext == ".inc" || ext == ".sbt";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
||||
bool
|
||||
|
||||
bake_scenario_tree(
|
||||
|
||||
std::string const &text_scenario_path,
|
||||
|
||||
unsigned const max_threads,
|
||||
|
||||
std::string &error_out ) {
|
||||
|
||||
error_out.clear();
|
||||
|
||||
|
||||
|
||||
fs::path const input { text_scenario_path };
|
||||
|
||||
if( false == fs::exists( input ) ) {
|
||||
|
||||
error_out = "brak pliku: " + text_scenario_path;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if( false == is_text_scenery_path( input ) ) {
|
||||
|
||||
error_out = "nie jest plikiem SCM/SCN/INC: " + text_scenario_path;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
|
||||
eu07::scene::bake::BakeTreeOptions options;
|
||||
|
||||
options.maxThreads = max_threads;
|
||||
|
||||
options.onProgress = log_bake_progress;
|
||||
|
||||
|
||||
|
||||
eu07::scene::bake::BakeTreeStats stats;
|
||||
|
||||
(void)eu07::scene::bake::bakeModuleTree( input, &stats, options );
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
catch( std::exception const &ex ) {
|
||||
|
||||
error_out = ex.what();
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
catch( ... ) {
|
||||
|
||||
error_out = "nieznany wyjatek bake";
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace scene::eu7::bake_parser
|
||||
|
||||
22
scene/eu7/eu7_bake_parser.h
Normal file
22
scene/eu7/eu7_bake_parser.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace scene::eu7::bake_parser {
|
||||
|
||||
[[nodiscard]] bool
|
||||
bake_scenario_tree(
|
||||
std::string const &TextScenarioPath,
|
||||
unsigned MaxThreads,
|
||||
std::string &ErrorOut );
|
||||
|
||||
} // namespace scene::eu7::bake_parser
|
||||
62
scene/eu7/eu7_chunks.h
Normal file
62
scene/eu7/eu7_chunks.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace scene::eu7 {
|
||||
namespace detail {
|
||||
|
||||
[[nodiscard]] constexpr std::uint32_t
|
||||
make_id4( char const a, char const b, char const c, char const d ) noexcept {
|
||||
return (
|
||||
( static_cast<std::uint32_t>( static_cast<unsigned char>( d ) ) << 24u ) |
|
||||
( static_cast<std::uint32_t>( static_cast<unsigned char>( c ) ) << 16u ) |
|
||||
( static_cast<std::uint32_t>( static_cast<unsigned char>( b ) ) << 8u ) |
|
||||
static_cast<std::uint32_t>( static_cast<unsigned char>( a ) ) );
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
constexpr std::uint32_t kEu7Magic { detail::make_id4( 'E', 'U', '7', 'B' ) };
|
||||
constexpr std::uint32_t kEu7VersionV4 { 4 };
|
||||
constexpr std::uint32_t kEu7VersionV5 { 5 };
|
||||
constexpr std::uint32_t kEu7VersionV6 { 6 };
|
||||
constexpr std::uint32_t kEu7VersionV7 { 7 };
|
||||
constexpr std::uint32_t kEu7VersionV8 { 8 };
|
||||
|
||||
constexpr std::uint32_t kChunkStrs { detail::make_id4( 'S', 'T', 'R', 'S' ) };
|
||||
constexpr std::uint32_t kChunkIncl { detail::make_id4( 'I', 'N', 'C', 'L' ) };
|
||||
constexpr std::uint32_t kChunkTrak { detail::make_id4( 'T', 'R', 'A', 'K' ) };
|
||||
constexpr std::uint32_t kChunkTrac { detail::make_id4( 'T', 'R', 'A', 'C' ) };
|
||||
constexpr std::uint32_t kChunkPwrs { detail::make_id4( 'P', 'W', 'R', 'S' ) };
|
||||
constexpr std::uint32_t kChunkTerr { detail::make_id4( 'T', 'E', 'R', 'R' ) };
|
||||
constexpr std::uint32_t kChunkMesh { detail::make_id4( 'M', 'E', 'S', 'H' ) };
|
||||
constexpr std::uint32_t kChunkLine { detail::make_id4( 'L', 'I', 'N', 'E' ) };
|
||||
constexpr std::uint32_t kChunkModl { detail::make_id4( 'M', 'O', 'D', 'L' ) };
|
||||
constexpr std::uint32_t kChunkMemc { detail::make_id4( 'M', 'E', 'M', 'C' ) };
|
||||
constexpr std::uint32_t kChunkLaun { detail::make_id4( 'L', 'A', 'U', 'N' ) };
|
||||
constexpr std::uint32_t kChunkDynm { detail::make_id4( 'D', 'Y', 'N', 'M' ) };
|
||||
constexpr std::uint32_t kChunkSond { detail::make_id4( 'S', 'O', 'N', 'D' ) };
|
||||
constexpr std::uint32_t kChunkTrset { detail::make_id4( 'T', 'R', 'S', 'E' ) };
|
||||
constexpr std::uint32_t kChunkEvnt { detail::make_id4( 'E', 'V', 'N', 'T' ) };
|
||||
constexpr std::uint32_t kChunkFint { detail::make_id4( 'F', 'I', 'N', 'T' ) };
|
||||
constexpr std::uint32_t kChunkPlac { detail::make_id4( 'P', 'L', 'A', 'C' ) };
|
||||
|
||||
// Indeks sekcji 1 km -> offset w chunku PACK (EU7B v7+).
|
||||
constexpr std::uint32_t kChunkPidx { detail::make_id4( 'P', 'I', 'D', 'X' ) };
|
||||
// MODL per sekcja, world-space; payload = concat sekcji (offsety z PIDX).
|
||||
constexpr std::uint32_t kChunkPack { detail::make_id4( 'P', 'A', 'C', 'K' ) };
|
||||
// Wspolne definicje modeli (EU7B v8+).
|
||||
constexpr std::uint32_t kChunkProt { detail::make_id4( 'P', 'R', 'O', 'T' ) };
|
||||
|
||||
constexpr std::uint8_t kPackSectionFormatV8 { 1 };
|
||||
|
||||
} // namespace scene::eu7
|
||||
790
scene/eu7/eu7_emit.cpp
Normal file
790
scene/eu7/eu7_emit.cpp
Normal file
@@ -0,0 +1,790 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "scene/eu7/eu7_emit.h"
|
||||
#include "scene/eu7/eu7_transform.h"
|
||||
#include "utilities/utilities.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
namespace scene::eu7 {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] std::string
|
||||
format_double( double const value ) {
|
||||
std::string text { std::format( "{:.4f}", value ) };
|
||||
if( auto const dot { text.find( '.' ) }; dot != std::string::npos ) {
|
||||
while( text.size() > dot + 1 && text.back() == '0' ) {
|
||||
text.pop_back();
|
||||
}
|
||||
if( text.back() == '.' ) {
|
||||
text.pop_back();
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
class TextEmitter {
|
||||
public:
|
||||
void
|
||||
push( std::string value ) {
|
||||
if( !first_on_line_ ) {
|
||||
out_ << ' ';
|
||||
}
|
||||
first_on_line_ = false;
|
||||
out_ << value;
|
||||
}
|
||||
|
||||
void
|
||||
push( std::string_view const value ) {
|
||||
push( std::string( value ) );
|
||||
}
|
||||
|
||||
void
|
||||
push( char const *const value ) {
|
||||
push( std::string_view( value ) );
|
||||
}
|
||||
|
||||
void
|
||||
push( double const value ) {
|
||||
push( format_double( value ) );
|
||||
}
|
||||
|
||||
void
|
||||
push( float const value ) {
|
||||
push( static_cast<double>( value ) );
|
||||
}
|
||||
|
||||
void
|
||||
push( int const value ) {
|
||||
push( std::to_string( value ) );
|
||||
}
|
||||
|
||||
void
|
||||
push_vec3( glm::dvec3 const &v ) {
|
||||
push( v.x );
|
||||
push( v.y );
|
||||
push( v.z );
|
||||
}
|
||||
|
||||
void
|
||||
newline() {
|
||||
out_ << '\n';
|
||||
first_on_line_ = true;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
str() const {
|
||||
return out_.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream out_;
|
||||
bool first_on_line_ { true };
|
||||
};
|
||||
|
||||
struct TransformEmitState {
|
||||
std::vector<glm::dvec3> origin_stack;
|
||||
std::vector<glm::dvec3> scale_stack;
|
||||
glm::dvec3 rotation{};
|
||||
std::size_t group_depth = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] glm::dvec3
|
||||
origin_push_delta( std::vector<glm::dvec3> const &stack, std::size_t const index ) {
|
||||
auto const &cumulative { stack[ index ] };
|
||||
if( index == 0 ) {
|
||||
return cumulative;
|
||||
}
|
||||
auto const &parent { stack[ index - 1 ] };
|
||||
return { cumulative.x - parent.x, cumulative.y - parent.y, cumulative.z - parent.z };
|
||||
}
|
||||
|
||||
[[nodiscard]] glm::dvec3
|
||||
scale_push_factor( std::vector<glm::dvec3> const &stack, std::size_t const index ) {
|
||||
auto const &cumulative { stack[ index ] };
|
||||
auto const parent { index == 0 ? glm::dvec3{ 1.0, 1.0, 1.0 } : stack[ index - 1 ] };
|
||||
return {
|
||||
parent.x != 0.0 ? cumulative.x / parent.x : cumulative.x,
|
||||
parent.y != 0.0 ? cumulative.y / parent.y : cumulative.y,
|
||||
parent.z != 0.0 ? cumulative.z / parent.z : cumulative.z };
|
||||
}
|
||||
|
||||
void
|
||||
sync_transform_stack( TextEmitter &w, TransformEmitState &state, Eu7TransformContext const &target ) {
|
||||
while( state.group_depth > target.group_depth ) {
|
||||
w.push( "endgroup" );
|
||||
--state.group_depth;
|
||||
}
|
||||
while( state.scale_stack.size() > target.scale_stack.size() ) {
|
||||
w.push( "endscale" );
|
||||
state.scale_stack.pop_back();
|
||||
}
|
||||
while( state.origin_stack.size() > target.origin_stack.size() ) {
|
||||
w.push( "endorigin" );
|
||||
state.origin_stack.pop_back();
|
||||
}
|
||||
|
||||
while( state.origin_stack.size() < target.origin_stack.size() ) {
|
||||
auto const index { state.origin_stack.size() };
|
||||
auto const delta { origin_push_delta( target.origin_stack, index ) };
|
||||
w.push( "origin" );
|
||||
w.push_vec3( delta );
|
||||
state.origin_stack.push_back( target.origin_stack[ index ] );
|
||||
}
|
||||
|
||||
while( state.scale_stack.size() < target.scale_stack.size() ) {
|
||||
auto const index { state.scale_stack.size() };
|
||||
auto const factor { scale_push_factor( target.scale_stack, index ) };
|
||||
w.push( "scale" );
|
||||
w.push_vec3( factor );
|
||||
state.scale_stack.push_back( target.scale_stack[ index ] );
|
||||
}
|
||||
|
||||
while( state.group_depth < target.group_depth ) {
|
||||
w.push( "group" );
|
||||
++state.group_depth;
|
||||
}
|
||||
|
||||
if( state.rotation.x != target.rotation.x || state.rotation.y != target.rotation.y ||
|
||||
state.rotation.z != target.rotation.z ) {
|
||||
w.push( "rotate" );
|
||||
w.push_vec3( target.rotation );
|
||||
state.rotation = target.rotation;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
emit_node_transform( TextEmitter &w, TransformEmitState &state, Eu7BasicNode const &node ) {
|
||||
sync_transform_stack( w, state, node.transform );
|
||||
}
|
||||
|
||||
[[nodiscard]] double
|
||||
range_max_from_node( Eu7BasicNode const &node ) {
|
||||
if( node.range_squared_max >= std::numeric_limits<double>::max() * 0.5 ) {
|
||||
return -1.0;
|
||||
}
|
||||
return std::sqrt( node.range_squared_max );
|
||||
}
|
||||
|
||||
[[nodiscard]] double
|
||||
range_min_from_node( Eu7BasicNode const &node ) {
|
||||
return std::sqrt( node.range_squared_min );
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
node_display_name( Eu7BasicNode const &node ) {
|
||||
return node.name.empty() ? "none" : node.name;
|
||||
}
|
||||
|
||||
void
|
||||
push_node_header( TextEmitter &w, Eu7BasicNode const &node ) {
|
||||
w.push( "node" );
|
||||
w.push( range_max_from_node( node ) );
|
||||
w.push( range_min_from_node( node ) );
|
||||
w.push( node_display_name( node ) );
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view
|
||||
track_environment_name( Eu7TrackEnvironment const env ) {
|
||||
switch( env ) {
|
||||
case Eu7TrackEnvironment::Flat:
|
||||
return "flat";
|
||||
case Eu7TrackEnvironment::Mountains:
|
||||
return "mountains";
|
||||
case Eu7TrackEnvironment::Canyon:
|
||||
return "canyon";
|
||||
case Eu7TrackEnvironment::Tunnel:
|
||||
return "tunnel";
|
||||
case Eu7TrackEnvironment::Bridge:
|
||||
return "bridge";
|
||||
case Eu7TrackEnvironment::Bank:
|
||||
return "bank";
|
||||
default:
|
||||
return "flat";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view
|
||||
track_kind_token( Eu7Track const &track ) {
|
||||
if( track.category == Eu7TrackCategory::Road ) {
|
||||
return "road";
|
||||
}
|
||||
if( track.category == Eu7TrackCategory::Water ) {
|
||||
return "river";
|
||||
}
|
||||
switch( track.track_type ) {
|
||||
case Eu7TrackType::Switch:
|
||||
return "switch";
|
||||
case Eu7TrackType::Cross:
|
||||
return "cross";
|
||||
case Eu7TrackType::Table:
|
||||
return "turn";
|
||||
case Eu7TrackType::Tributary:
|
||||
return "tributary";
|
||||
default:
|
||||
return "normal";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
push_track_core( TextEmitter &w, Eu7Track const &track ) {
|
||||
w.push( track.length );
|
||||
w.push( track.track_width );
|
||||
w.push( track.friction );
|
||||
w.push( track.sound_distance );
|
||||
w.push( track.quality_flag );
|
||||
w.push( track.damage_flag );
|
||||
w.push( track_environment_name( track.environment ) );
|
||||
if( track.visibility ) {
|
||||
w.push( "vis" );
|
||||
w.push( track.visibility->material1 );
|
||||
w.push( track.visibility->tex_length );
|
||||
w.push( track.visibility->material2 );
|
||||
w.push( track.visibility->tex_height1 );
|
||||
w.push( track.visibility->tex_width );
|
||||
w.push( track.visibility->tex_slope );
|
||||
}
|
||||
else {
|
||||
w.push( "unvis" );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
push_track_segments( TextEmitter &w, Eu7Track const &track, Eu7TransformContext const &transform ) {
|
||||
for( auto const &seg : track.paths ) {
|
||||
w.push_vec3( subtract_origin_offset( seg.p_start, transform ) );
|
||||
w.push( seg.roll_start );
|
||||
w.push_vec3( seg.cp_out );
|
||||
w.push_vec3( seg.cp_in );
|
||||
w.push_vec3( subtract_origin_offset( seg.p_end, transform ) );
|
||||
w.push( seg.roll_end );
|
||||
w.push( seg.radius );
|
||||
}
|
||||
for( auto const &[key, value] : track.tail_keywords ) {
|
||||
w.push( key );
|
||||
if( key == "sleepermodel" ) {
|
||||
std::size_t start { 0 };
|
||||
while( start < value.size() ) {
|
||||
auto const end { value.find( ' ', start ) };
|
||||
if( end == std::string::npos ) {
|
||||
w.push( value.substr( start ) );
|
||||
break;
|
||||
}
|
||||
w.push( value.substr( start, end - start ) );
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
w.push( value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
emit_track( TextEmitter &w, TransformEmitState &state, Eu7Track const &track ) {
|
||||
emit_node_transform( w, state, track.node );
|
||||
push_node_header( w, track.node );
|
||||
w.push( "track" );
|
||||
w.push( track_kind_token( track ) );
|
||||
push_track_core( w, track );
|
||||
push_track_segments( w, track, track.node.transform );
|
||||
w.push( "endtrack" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
traction_resistivity_token( Eu7Traction const &traction ) {
|
||||
if( traction.resistivity_legacy != 0.0 ) {
|
||||
return format_double( traction.resistivity_legacy );
|
||||
}
|
||||
return format_double( traction.resistivity_ohm_per_m / 0.001 );
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
traction_material_token( Eu7Traction const &traction ) {
|
||||
if( !traction.material_raw.empty() ) {
|
||||
return traction.material_raw;
|
||||
}
|
||||
switch( traction.material ) {
|
||||
case Eu7TractionWireMaterial::Aluminium:
|
||||
return "al";
|
||||
case Eu7TractionWireMaterial::None:
|
||||
return "none";
|
||||
default:
|
||||
return "cu";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
emit_traction( TextEmitter &w, TransformEmitState &state, Eu7Traction const &traction ) {
|
||||
emit_node_transform( w, state, traction.node );
|
||||
auto const &transform { traction.node.transform };
|
||||
push_node_header( w, traction.node );
|
||||
w.push( "traction" );
|
||||
w.push( traction.power_supply_name );
|
||||
w.push( traction.nominal_voltage );
|
||||
w.push( traction.max_current );
|
||||
w.push( traction_resistivity_token( traction ) );
|
||||
w.push( traction_material_token( traction ) );
|
||||
w.push( traction.wire_thickness );
|
||||
w.push( traction.damage_flag );
|
||||
w.push_vec3( subtract_origin_offset( traction.wire_p1, transform ) );
|
||||
w.push_vec3( subtract_origin_offset( traction.wire_p2, transform ) );
|
||||
w.push_vec3( subtract_origin_offset( traction.wire_p3, transform ) );
|
||||
w.push_vec3( subtract_origin_offset( traction.wire_p4, transform ) );
|
||||
w.push( traction.min_height );
|
||||
w.push( traction.segment_length );
|
||||
w.push( traction.wire_count );
|
||||
w.push( traction.wire_offset );
|
||||
w.push( traction.node.visible ? "vis" : "unvis" );
|
||||
if( traction.parallel_name ) {
|
||||
w.push( "parallel" );
|
||||
w.push( *traction.parallel_name );
|
||||
}
|
||||
w.push( "endtraction" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
void
|
||||
emit_power_source( TextEmitter &w, TransformEmitState &state, Eu7TractionPowerSource const &source ) {
|
||||
emit_node_transform( w, state, source.node );
|
||||
push_node_header( w, source.node );
|
||||
w.push( "tractionpowersource" );
|
||||
w.push_vec3( inverse_transform_point( source.position, source.node.transform ) );
|
||||
w.push( source.nominal_voltage );
|
||||
w.push( source.voltage_frequency );
|
||||
w.push( source.internal_resistance_legacy );
|
||||
w.push( source.max_output_current );
|
||||
w.push( source.fast_fuse_timeout );
|
||||
w.push( source.fast_fuse_repetition );
|
||||
w.push( source.slow_fuse_timeout );
|
||||
if( source.modifier == Eu7PowerSourceModifier::Recuperation ) {
|
||||
w.push( "recuperation" );
|
||||
}
|
||||
else if( source.modifier == Eu7PowerSourceModifier::Section ) {
|
||||
w.push( "section" );
|
||||
}
|
||||
w.push( "end" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
void
|
||||
emit_shape( TextEmitter &w, TransformEmitState &state, Eu7Shape const &shape ) {
|
||||
emit_node_transform( w, state, shape.node );
|
||||
auto local_vertices { shape.vertices };
|
||||
inverse_transform_shape_vertices( local_vertices, shape.node.transform );
|
||||
push_node_header( w, shape.node );
|
||||
auto const shape_type {
|
||||
shape.node.node_type.empty() ? std::string_view{ "triangles" } :
|
||||
std::string_view{ shape.node.node_type } };
|
||||
w.push( shape_type );
|
||||
w.push( shape.material_path );
|
||||
auto const triangle_list { shape_type == "triangles" };
|
||||
for( std::size_t i { 0 }; i < local_vertices.size(); ++i ) {
|
||||
auto const &vert { local_vertices[ i ] };
|
||||
w.push_vec3( vert.position );
|
||||
w.push( static_cast<double>( vert.normal.x ) );
|
||||
w.push( static_cast<double>( vert.normal.y ) );
|
||||
w.push( static_cast<double>( vert.normal.z ) );
|
||||
w.push( vert.u );
|
||||
w.push( vert.v );
|
||||
if( triangle_list && i % 3 != 2 ) {
|
||||
w.push( "end" );
|
||||
}
|
||||
}
|
||||
w.push( "endtri" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
void
|
||||
emit_lines( TextEmitter &w, TransformEmitState &state, Eu7Lines const &lines ) {
|
||||
emit_node_transform( w, state, lines.node );
|
||||
auto local_vertices { lines.vertices };
|
||||
inverse_transform_shape_vertices( local_vertices, lines.node.transform );
|
||||
push_node_header( w, lines.node );
|
||||
w.push( "lines" );
|
||||
w.push( lines.lighting.diffuse.x * 255.0 );
|
||||
w.push( lines.lighting.diffuse.y * 255.0 );
|
||||
w.push( lines.lighting.diffuse.z * 255.0 );
|
||||
w.push( lines.line_width );
|
||||
for( std::size_t i { 0 }; i + 1 < local_vertices.size(); i += 2 ) {
|
||||
w.push_vec3( local_vertices[ i ].position );
|
||||
w.push_vec3( local_vertices[ i + 1 ].position );
|
||||
}
|
||||
w.push( "endline" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
void
|
||||
emit_model( TextEmitter &w, TransformEmitState &state, Eu7Model const &model ) {
|
||||
emit_node_transform( w, state, model.node );
|
||||
auto const local_location { inverse_transform_point( model.location, model.node.transform ) };
|
||||
auto const local_rotation_y { model.angles.y - model.node.transform.rotation.y };
|
||||
if( model.is_terrain ) {
|
||||
w.push( "node" );
|
||||
w.push( -1.0 );
|
||||
w.push( 0.0 );
|
||||
w.push( node_display_name( model.node ) );
|
||||
}
|
||||
else {
|
||||
push_node_header( w, model.node );
|
||||
}
|
||||
w.push( "model" );
|
||||
w.push_vec3( local_location );
|
||||
w.push( local_rotation_y );
|
||||
w.push( model.model_file );
|
||||
w.push( model.texture_file );
|
||||
w.push( "endmodel" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
void
|
||||
emit_memcell( TextEmitter &w, TransformEmitState &state, Eu7MemCell const &cell ) {
|
||||
emit_node_transform( w, state, cell.node );
|
||||
push_node_header( w, cell.node );
|
||||
w.push( "memcell" );
|
||||
w.push_vec3( inverse_transform_point( cell.node.area.center, cell.node.transform ) );
|
||||
w.push( cell.text );
|
||||
w.push( cell.value1 );
|
||||
w.push( cell.value2 );
|
||||
w.push( cell.track_name.value_or( "none" ) );
|
||||
w.push( "endmemcell" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
launcher_time_token( Eu7EventLauncher const &launcher ) {
|
||||
if( launcher.launch_hour != 0 || launcher.launch_minute != 0 ) {
|
||||
return std::format( "{}", launcher.launch_hour * 100 + launcher.launch_minute );
|
||||
}
|
||||
if( launcher.delta_time != 0.0 ) {
|
||||
return format_double( -launcher.delta_time );
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
|
||||
void
|
||||
emit_launcher( TextEmitter &w, TransformEmitState &state, Eu7EventLauncher const &launcher ) {
|
||||
emit_node_transform( w, state, launcher.node );
|
||||
push_node_header( w, launcher.node );
|
||||
w.push( "eventlauncher" );
|
||||
w.push_vec3( inverse_transform_point( launcher.location, launcher.node.transform ) );
|
||||
w.push( std::sqrt( launcher.radius_squared ) );
|
||||
w.push( launcher.activation_key_raw );
|
||||
w.push( launcher_time_token( launcher ) );
|
||||
w.push( launcher.event1_name );
|
||||
if( !launcher.event2_name.empty() && launcher.event2_name != "none" ) {
|
||||
w.push( launcher.event2_name );
|
||||
}
|
||||
if( launcher.condition ) {
|
||||
w.push( "condition" );
|
||||
w.push( launcher.condition->memcell_name );
|
||||
w.push( launcher.condition->compare_text );
|
||||
if( launcher.condition->check_mask & 2 ) {
|
||||
w.push( launcher.condition->compare_value1 );
|
||||
}
|
||||
else {
|
||||
w.push( "*" );
|
||||
}
|
||||
if( launcher.condition->check_mask & 4 ) {
|
||||
w.push( launcher.condition->compare_value2 );
|
||||
}
|
||||
else {
|
||||
w.push( "*" );
|
||||
}
|
||||
}
|
||||
if( launcher.train_triggered ) {
|
||||
w.push( "traintriggered" );
|
||||
}
|
||||
w.push( "endeventlauncher" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
void
|
||||
emit_dynamic( TextEmitter &w, TransformEmitState &state, Eu7Dynamic const &vehicle, bool const in_trainset ) {
|
||||
emit_node_transform( w, state, vehicle.node );
|
||||
push_node_header( w, vehicle.node );
|
||||
w.push( "dynamic" );
|
||||
w.push( vehicle.data_folder );
|
||||
w.push( vehicle.skin_file );
|
||||
w.push( vehicle.mmd_file );
|
||||
if( !in_trainset ) {
|
||||
w.push( vehicle.track_name );
|
||||
}
|
||||
w.push( vehicle.offset );
|
||||
w.push( vehicle.driver_type );
|
||||
if( in_trainset ) {
|
||||
w.push( vehicle.coupling_raw.empty() ? std::to_string( vehicle.coupling ) : vehicle.coupling_raw );
|
||||
}
|
||||
if( !in_trainset ) {
|
||||
w.push( vehicle.velocity );
|
||||
}
|
||||
w.push( vehicle.load_count );
|
||||
if( !vehicle.load_type.empty() ) {
|
||||
w.push( vehicle.load_type );
|
||||
}
|
||||
if( vehicle.destination ) {
|
||||
w.push( *vehicle.destination );
|
||||
}
|
||||
w.push( "enddynamic" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
void
|
||||
emit_sound( TextEmitter &w, TransformEmitState &state, Eu7Sound const &sound ) {
|
||||
emit_node_transform( w, state, sound.node );
|
||||
push_node_header( w, sound.node );
|
||||
w.push( "sound" );
|
||||
w.push_vec3( inverse_transform_point( sound.location, sound.node.transform ) );
|
||||
w.push( sound.wav_file );
|
||||
w.push( "endsound" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view
|
||||
event_type_name( Eu7EventType const type ) {
|
||||
switch( type ) {
|
||||
case Eu7EventType::AddValues:
|
||||
return "addvalues";
|
||||
case Eu7EventType::UpdateValues:
|
||||
return "updatevalues";
|
||||
case Eu7EventType::CopyValues:
|
||||
return "copyvalues";
|
||||
case Eu7EventType::GetValues:
|
||||
return "getvalues";
|
||||
case Eu7EventType::PutValues:
|
||||
return "putvalues";
|
||||
case Eu7EventType::Whois:
|
||||
return "whois";
|
||||
case Eu7EventType::LogValues:
|
||||
return "logvalues";
|
||||
case Eu7EventType::Multiple:
|
||||
return "multiple";
|
||||
case Eu7EventType::Switch:
|
||||
return "switch";
|
||||
case Eu7EventType::TrackVel:
|
||||
return "trackvel";
|
||||
case Eu7EventType::Sound:
|
||||
return "sound";
|
||||
case Eu7EventType::Texture:
|
||||
return "texture";
|
||||
case Eu7EventType::Animation:
|
||||
return "animation";
|
||||
case Eu7EventType::Lights:
|
||||
return "lights";
|
||||
case Eu7EventType::Voltage:
|
||||
return "voltage";
|
||||
case Eu7EventType::Visible:
|
||||
return "visible";
|
||||
case Eu7EventType::Friction:
|
||||
return "friction";
|
||||
case Eu7EventType::Message:
|
||||
return "message";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
join_targets( std::vector<std::string> const &targets ) {
|
||||
std::string joined;
|
||||
for( std::size_t i { 0 }; i < targets.size(); ++i ) {
|
||||
if( i > 0 ) {
|
||||
joined += '|';
|
||||
}
|
||||
joined += targets[ i ];
|
||||
}
|
||||
return joined.empty() ? "none" : joined;
|
||||
}
|
||||
|
||||
void
|
||||
emit_event( TextEmitter &w, Eu7Event const &event ) {
|
||||
w.push( "event" );
|
||||
if( event.ignored ) {
|
||||
w.push( std::string{ "none_" } + event.name );
|
||||
}
|
||||
else {
|
||||
w.push( event.name );
|
||||
}
|
||||
w.push( event_type_name( event.type ) );
|
||||
w.push( event.delay );
|
||||
w.push( join_targets( event.targets ) );
|
||||
for( auto const &[key, value] : event.payload ) {
|
||||
if( !key.empty() ) {
|
||||
w.push( key );
|
||||
}
|
||||
if( !value.empty() ) {
|
||||
w.push( value );
|
||||
}
|
||||
}
|
||||
if( event.delay_random != 0.0 ) {
|
||||
w.push( "randomdelay" );
|
||||
w.push( event.delay_random );
|
||||
}
|
||||
if( event.delay_departure != 0.0 ) {
|
||||
w.push( "departuredelay" );
|
||||
w.push( event.delay_departure );
|
||||
}
|
||||
if( event.passive ) {
|
||||
w.push( "passive" );
|
||||
}
|
||||
w.push( "endevent" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
void
|
||||
emit_trainset_block(
|
||||
TextEmitter &w,
|
||||
TransformEmitState &state,
|
||||
Eu7Trainset const &trainset,
|
||||
std::vector<Eu7Dynamic> const &dynamics ) {
|
||||
w.push( "trainset" );
|
||||
w.push( trainset.name );
|
||||
w.push( trainset.track );
|
||||
w.push( trainset.offset );
|
||||
w.push( trainset.velocity );
|
||||
if( false == trainset.assignment.empty() ) {
|
||||
w.push( "assignment" );
|
||||
for( auto const &[lang, assignment] : trainset.assignment ) {
|
||||
w.push( lang );
|
||||
w.push( '"' + assignment + '"' );
|
||||
}
|
||||
w.push( "endassignment" );
|
||||
}
|
||||
std::size_t vehicle_slot { 0 };
|
||||
for( auto const index : trainset.vehicle_indices ) {
|
||||
if( index < dynamics.size() ) {
|
||||
auto vehicle { dynamics[ index ] };
|
||||
if( vehicle_slot < trainset.couplings.size() ) {
|
||||
vehicle.coupling_raw = std::to_string( trainset.couplings[ vehicle_slot ] );
|
||||
}
|
||||
emit_dynamic( w, state, vehicle, true );
|
||||
++vehicle_slot;
|
||||
}
|
||||
}
|
||||
w.push( "endtrainset" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string
|
||||
include_text_path( Eu7Include const &include ) {
|
||||
if( !include.source_path.empty() ) {
|
||||
return include.source_path;
|
||||
}
|
||||
auto path { include.binary_path };
|
||||
replace_slashes( path );
|
||||
if( path.ends_with( ".eu7" ) ) {
|
||||
path.replace( path.size() - 4, 4, ".inc" );
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string
|
||||
emit_includes_text( std::vector<Eu7Include> const &includes ) {
|
||||
if( includes.empty() ) {
|
||||
return {};
|
||||
}
|
||||
TextEmitter w;
|
||||
for( auto const &include : includes ) {
|
||||
w.push( "include" );
|
||||
w.push( include_text_path( include ) );
|
||||
for( auto const ¶m : include.parameters ) {
|
||||
w.push( param );
|
||||
}
|
||||
w.push( "end" );
|
||||
w.newline();
|
||||
}
|
||||
return w.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
emit_scene_objects_text( Eu7Module const &module ) {
|
||||
TextEmitter w;
|
||||
TransformEmitState transform_state;
|
||||
auto const &scene { module.scene };
|
||||
|
||||
for( auto const &track : scene.tracks ) {
|
||||
emit_track( w, transform_state, track );
|
||||
}
|
||||
for( auto const &traction : scene.traction ) {
|
||||
emit_traction( w, transform_state, traction );
|
||||
}
|
||||
for( auto const &source : scene.power_sources ) {
|
||||
emit_power_source( w, transform_state, source );
|
||||
}
|
||||
for( auto const &model : scene.models ) {
|
||||
emit_model( w, transform_state, model );
|
||||
}
|
||||
for( auto const &cell : scene.memcells ) {
|
||||
emit_memcell( w, transform_state, cell );
|
||||
}
|
||||
for( auto const &launcher : scene.event_launchers ) {
|
||||
emit_launcher( w, transform_state, launcher );
|
||||
}
|
||||
|
||||
std::vector<bool> emitted_in_trainset( scene.dynamics.size(), false );
|
||||
for( auto const &trainset : scene.trainsets ) {
|
||||
for( auto const index : trainset.vehicle_indices ) {
|
||||
if( index < emitted_in_trainset.size() ) {
|
||||
emitted_in_trainset[ index ] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for( std::size_t i { 0 }; i < scene.dynamics.size(); ++i ) {
|
||||
if( !emitted_in_trainset[ i ] ) {
|
||||
emit_dynamic( w, transform_state, scene.dynamics[ i ], false );
|
||||
}
|
||||
}
|
||||
|
||||
for( auto const &sound : scene.sounds ) {
|
||||
emit_sound( w, transform_state, sound );
|
||||
}
|
||||
|
||||
for( auto const &event : scene.events ) {
|
||||
emit_event( w, event );
|
||||
}
|
||||
|
||||
for( std::uint32_t i { 0 }; i < scene.first_init_count; ++i ) {
|
||||
w.push( "FirstInit" );
|
||||
w.newline();
|
||||
}
|
||||
|
||||
for( auto const &trainset : scene.trainsets ) {
|
||||
emit_trainset_block( w, transform_state, trainset, scene.dynamics );
|
||||
}
|
||||
|
||||
return w.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
emit_module_text( Eu7Module const &module ) {
|
||||
TextEmitter w;
|
||||
TransformEmitState transform_state;
|
||||
auto const &scene { module.scene };
|
||||
|
||||
for( auto const &shape : scene.shapes ) {
|
||||
emit_shape( w, transform_state, shape );
|
||||
}
|
||||
for( auto const &lines : scene.lines ) {
|
||||
emit_lines( w, transform_state, lines );
|
||||
}
|
||||
|
||||
std::string text { w.str() };
|
||||
text += emit_scene_objects_text( module );
|
||||
return text;
|
||||
}
|
||||
|
||||
} // namespace scene::eu7
|
||||
34
scene/eu7/eu7_emit.h
Normal file
34
scene/eu7/eu7_emit.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/eu7/eu7_types.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
// Sciezka tekstowa dla fallbacku include (source_path albo .eu7 -> .inc).
|
||||
[[nodiscard]] std::string
|
||||
include_text_path( Eu7Include const &Include );
|
||||
|
||||
// Emituje bloki "include ... end" dla parsera SCM.
|
||||
[[nodiscard]] std::string
|
||||
emit_includes_text( std::vector<Eu7Include> const &Includes );
|
||||
|
||||
// Emituje zawartosc modulu (bez INCL i bez terrain_shapes) do tekstu SCM.
|
||||
[[nodiscard]] std::string
|
||||
emit_module_text( Eu7Module const &Module );
|
||||
|
||||
// Torow, eventy, modele itd. — bez TERR/MESH/LINE (te ida bezposrednio z binarki).
|
||||
[[nodiscard]] std::string
|
||||
emit_scene_objects_text( Eu7Module const &Module );
|
||||
|
||||
} // namespace scene::eu7
|
||||
148
scene/eu7/eu7_load_stats.cpp
Normal file
148
scene/eu7/eu7_load_stats.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "scene/eu7/eu7_load_stats.h"
|
||||
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace scene::eu7 {
|
||||
namespace {
|
||||
|
||||
Eu7LoadStats g_load_stats;
|
||||
|
||||
[[nodiscard]] std::string
|
||||
format_seconds( double const ms ) {
|
||||
std::ostringstream out;
|
||||
out << std::fixed << std::setprecision( 1 ) << ( ms / 1000.0 ) << 's';
|
||||
return out.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
double
|
||||
Eu7LoadStats::total_ms() const {
|
||||
return (
|
||||
read_ms +
|
||||
scm_fallback_ms +
|
||||
place_fast_ms +
|
||||
place_full_ms +
|
||||
terr_ms +
|
||||
mesh_ms +
|
||||
line_ms +
|
||||
trak_ms +
|
||||
trac_ms +
|
||||
power_ms +
|
||||
model_ms +
|
||||
memcell_ms +
|
||||
launcher_ms +
|
||||
dynamic_ms +
|
||||
sound_ms +
|
||||
event_ms +
|
||||
trainset_ms +
|
||||
first_init_ms );
|
||||
}
|
||||
|
||||
ScopedTimer::ScopedTimer( double &target )
|
||||
: m_target { target }
|
||||
, m_start { std::chrono::steady_clock::now() } {}
|
||||
|
||||
ScopedTimer::~ScopedTimer() {
|
||||
auto const end { std::chrono::steady_clock::now() };
|
||||
m_target += std::chrono::duration<double, std::milli>( end - m_start ).count();
|
||||
}
|
||||
|
||||
Eu7LoadStats &
|
||||
load_stats() {
|
||||
return g_load_stats;
|
||||
}
|
||||
|
||||
void
|
||||
reset_load_stats() {
|
||||
g_load_stats = {};
|
||||
}
|
||||
|
||||
void
|
||||
log_load_stats() {
|
||||
auto const &s { g_load_stats };
|
||||
auto const total { s.total_ms() };
|
||||
|
||||
WriteLog( "EU7 load stats (accounted " + format_seconds( total ) + "):" );
|
||||
WriteLog(
|
||||
" read/cache: " + format_seconds( s.read_ms ) +
|
||||
" reads=" + std::to_string( s.module_read ) +
|
||||
" cache_hit=" + std::to_string( s.module_cache_hit ) );
|
||||
WriteLog(
|
||||
" scm fallback: " + format_seconds( s.scm_fallback_ms ) +
|
||||
" count=" + std::to_string( s.scm_fallback ) );
|
||||
WriteLog(
|
||||
" place fast (MODL-only): " + format_seconds( s.place_fast_ms ) +
|
||||
" paths=" + std::to_string( s.model_fast_path ) );
|
||||
WriteLog(
|
||||
" place full: " + format_seconds( s.place_full_ms ) +
|
||||
" paths=" + std::to_string( s.module_full_path ) );
|
||||
WriteLog(
|
||||
" terr: " + format_seconds( s.terr_ms ) );
|
||||
WriteLog(
|
||||
" mesh: " + format_seconds( s.mesh_ms ) );
|
||||
WriteLog(
|
||||
" line: " + format_seconds( s.line_ms ) );
|
||||
WriteLog(
|
||||
" trak: " + format_seconds( s.trak_ms ) +
|
||||
" count=" + std::to_string( s.tracks ) );
|
||||
WriteLog(
|
||||
" trac: " + format_seconds( s.trac_ms ) +
|
||||
" count=" + std::to_string( s.traction ) );
|
||||
WriteLog(
|
||||
" power: " + format_seconds( s.power_ms ) +
|
||||
" count=" + std::to_string( s.power_sources ) );
|
||||
WriteLog(
|
||||
" model insert: " + format_seconds( s.model_ms ) +
|
||||
" count=" + std::to_string( s.models ) );
|
||||
WriteLog(
|
||||
" memcell: " + format_seconds( s.memcell_ms ) +
|
||||
" count=" + std::to_string( s.memcells ) );
|
||||
WriteLog(
|
||||
" launcher: " + format_seconds( s.launcher_ms ) +
|
||||
" count=" + std::to_string( s.launchers ) );
|
||||
WriteLog(
|
||||
" dynamic: " + format_seconds( s.dynamic_ms ) +
|
||||
" count=" + std::to_string( s.dynamics ) );
|
||||
WriteLog(
|
||||
" sound: " + format_seconds( s.sound_ms ) +
|
||||
" count=" + std::to_string( s.sounds ) );
|
||||
WriteLog(
|
||||
" event: " + format_seconds( s.event_ms ) +
|
||||
" count=" + std::to_string( s.events ) );
|
||||
WriteLog(
|
||||
" trainset: " + format_seconds( s.trainset_ms ) +
|
||||
" count=" + std::to_string( s.trainsets ) );
|
||||
WriteLog(
|
||||
" first_init: " + format_seconds( s.first_init_ms ) );
|
||||
if(
|
||||
s.pack_sections_loaded > 0 || s.pack_skipped_includes > 0 ||
|
||||
s.pack_skipped_models > 0 ) {
|
||||
WriteLog(
|
||||
" pack: sections=" + std::to_string( s.pack_sections_loaded ) +
|
||||
" models=" + std::to_string( s.pack_models ) +
|
||||
" skipped_incl=" + std::to_string( s.pack_skipped_includes ) +
|
||||
" skipped_modl=" + std::to_string( s.pack_skipped_models ) );
|
||||
}
|
||||
WriteLog(
|
||||
" visits=" + std::to_string( s.module_visits ) +
|
||||
" applied=" + std::to_string( s.module_applied ) +
|
||||
" deduped=" + std::to_string( s.module_deduped ) );
|
||||
WriteLog(
|
||||
" (roznica vs Scenario loading time = deserialize_continue, map geometry, itd.)" );
|
||||
}
|
||||
|
||||
} // namespace scene::eu7
|
||||
85
scene/eu7/eu7_load_stats.h
Normal file
85
scene/eu7/eu7_load_stats.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
struct Eu7LoadStats {
|
||||
double read_ms { 0.0 };
|
||||
double scm_fallback_ms { 0.0 };
|
||||
double place_fast_ms { 0.0 };
|
||||
double place_full_ms { 0.0 };
|
||||
double terr_ms { 0.0 };
|
||||
double mesh_ms { 0.0 };
|
||||
double line_ms { 0.0 };
|
||||
double trak_ms { 0.0 };
|
||||
double trac_ms { 0.0 };
|
||||
double power_ms { 0.0 };
|
||||
double model_ms { 0.0 };
|
||||
double memcell_ms { 0.0 };
|
||||
double launcher_ms { 0.0 };
|
||||
double dynamic_ms { 0.0 };
|
||||
double sound_ms { 0.0 };
|
||||
double event_ms { 0.0 };
|
||||
double trainset_ms { 0.0 };
|
||||
double first_init_ms { 0.0 };
|
||||
|
||||
std::uint64_t module_visits { 0 };
|
||||
std::uint64_t module_applied { 0 };
|
||||
std::uint64_t module_deduped { 0 };
|
||||
std::uint64_t module_read { 0 };
|
||||
std::uint64_t module_cache_hit { 0 };
|
||||
std::uint64_t model_fast_path { 0 };
|
||||
std::uint64_t module_full_path { 0 };
|
||||
std::uint64_t scm_fallback { 0 };
|
||||
std::uint64_t models { 0 };
|
||||
std::uint64_t tracks { 0 };
|
||||
std::uint64_t traction { 0 };
|
||||
std::uint64_t power_sources { 0 };
|
||||
std::uint64_t memcells { 0 };
|
||||
std::uint64_t launchers { 0 };
|
||||
std::uint64_t dynamics { 0 };
|
||||
std::uint64_t sounds { 0 };
|
||||
std::uint64_t events { 0 };
|
||||
std::uint64_t trainsets { 0 };
|
||||
std::uint64_t pack_skipped_includes { 0 };
|
||||
std::uint64_t pack_sections_loaded { 0 };
|
||||
std::uint64_t pack_models { 0 };
|
||||
std::uint64_t pack_skipped_models { 0 };
|
||||
|
||||
[[nodiscard]] double total_ms() const;
|
||||
};
|
||||
|
||||
class ScopedTimer {
|
||||
public:
|
||||
explicit ScopedTimer( double &Target );
|
||||
~ScopedTimer();
|
||||
|
||||
ScopedTimer( ScopedTimer const & ) = delete;
|
||||
ScopedTimer &operator=( ScopedTimer const & ) = delete;
|
||||
|
||||
private:
|
||||
double &m_target;
|
||||
std::chrono::steady_clock::time_point m_start;
|
||||
};
|
||||
|
||||
[[nodiscard]] Eu7LoadStats &
|
||||
load_stats();
|
||||
|
||||
void
|
||||
reset_load_stats();
|
||||
|
||||
void
|
||||
log_load_stats();
|
||||
|
||||
} // namespace scene::eu7
|
||||
587
scene/eu7/eu7_loader.cpp
Normal file
587
scene/eu7/eu7_loader.cpp
Normal file
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "scene/eu7/eu7_loader.h"
|
||||
|
||||
#include "scene/eu7/eu7_apply.h"
|
||||
#include "scene/eu7/eu7_emit.h"
|
||||
#include "scene/eu7/eu7_load_stats.h"
|
||||
#include "scene/eu7/eu7_pack_bench.h"
|
||||
#include "scene/eu7/eu7_reader.h"
|
||||
#include "scene/eu7/eu7_section_stream.h"
|
||||
#include "scene/eu7/eu7_parameters.h"
|
||||
#include "scene/eu7/eu7_transform.h"
|
||||
#include "scene/scene.h"
|
||||
#include "scene/scenenode.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include "simulation/simulation.h"
|
||||
#include "simulation/simulationstateserializer.h"
|
||||
#include "scene/sn_utils.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "utilities/utilities.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
namespace {
|
||||
|
||||
std::unordered_map<std::string, Eu7Module> g_module_file_cache;
|
||||
bool g_packed_root_active { false };
|
||||
|
||||
void
|
||||
apply_material( shape_node::shapenode_data &data, std::string material_name ) {
|
||||
replace_slashes( material_name );
|
||||
data.material = GfxRenderer->Fetch_Material( material_name );
|
||||
|
||||
auto const texturehandle {
|
||||
data.material != null_handle ?
|
||||
GfxRenderer->Material( data.material )->GetTexture( 0 ) :
|
||||
null_handle };
|
||||
auto const &texture {
|
||||
texturehandle ?
|
||||
GfxRenderer->Texture( texturehandle ) :
|
||||
*ITexture::null_texture() };
|
||||
if( texturehandle != null_handle ) {
|
||||
data.translucent = (
|
||||
contains( texture.get_name(), '@' ) && texture.get_has_alpha() );
|
||||
}
|
||||
else {
|
||||
data.translucent = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
finalize_shape_bounds( shape_node &shape, shape_node::shapenode_data &data ) {
|
||||
if( data.vertices.empty() ) {
|
||||
return;
|
||||
}
|
||||
data.area.center = glm::dvec3( 0.0 );
|
||||
for( auto const &vertex : data.vertices ) {
|
||||
data.area.center += vertex.position;
|
||||
}
|
||||
data.area.center /=
|
||||
static_cast<double>( data.vertices.size() );
|
||||
shape.invalidate_radius();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
shape_node
|
||||
build_shape_node( Eu7Shape const &source ) {
|
||||
shape_node shape;
|
||||
auto &data { shape.m_data };
|
||||
data.rangesquared_min = source.node.range_squared_min;
|
||||
data.rangesquared_max = source.node.range_squared_max;
|
||||
data.visible = source.node.visible;
|
||||
data.origin = source.origin;
|
||||
data.lighting.diffuse = glm::vec4(
|
||||
source.lighting.diffuse.x, source.lighting.diffuse.y,
|
||||
source.lighting.diffuse.z, source.lighting.diffuse.w );
|
||||
data.lighting.ambient = glm::vec4(
|
||||
source.lighting.ambient.x, source.lighting.ambient.y,
|
||||
source.lighting.ambient.z, source.lighting.ambient.w );
|
||||
data.lighting.specular = glm::vec4(
|
||||
source.lighting.specular.x, source.lighting.specular.y,
|
||||
source.lighting.specular.z, source.lighting.specular.w );
|
||||
if( source.translucent ) {
|
||||
data.translucent = true;
|
||||
}
|
||||
apply_material( data, source.material_path );
|
||||
data.vertices.resize( source.vertices.size() );
|
||||
for( std::size_t i { 0 }; i < source.vertices.size(); ++i ) {
|
||||
auto const &src { source.vertices[ i ] };
|
||||
world_vertex &dst { data.vertices[ i ] };
|
||||
dst.position = src.position;
|
||||
dst.normal = src.normal;
|
||||
dst.texture = glm::vec2( static_cast<float>( src.u ), static_cast<float>( src.v ) );
|
||||
}
|
||||
finalize_shape_bounds( shape, data );
|
||||
return shape;
|
||||
}
|
||||
|
||||
lines_node
|
||||
build_lines_node( Eu7Lines const &source ) {
|
||||
lines_node node;
|
||||
auto &data { node.m_data };
|
||||
data.rangesquared_min = source.node.range_squared_min;
|
||||
data.rangesquared_max = source.node.range_squared_max;
|
||||
data.visible = source.node.visible;
|
||||
data.line_width = source.line_width;
|
||||
data.lighting.diffuse = glm::vec4(
|
||||
source.lighting.diffuse.x, source.lighting.diffuse.y,
|
||||
source.lighting.diffuse.z, source.lighting.diffuse.w );
|
||||
data.lighting.ambient = glm::vec4(
|
||||
source.lighting.ambient.x, source.lighting.ambient.y,
|
||||
source.lighting.ambient.z, source.lighting.ambient.w );
|
||||
data.lighting.specular = glm::vec4(
|
||||
source.lighting.specular.x, source.lighting.specular.y,
|
||||
source.lighting.specular.z, source.lighting.specular.w );
|
||||
data.origin = source.origin;
|
||||
data.vertices.resize( source.vertices.size() );
|
||||
for( std::size_t i { 0 }; i < source.vertices.size(); ++i ) {
|
||||
data.vertices[ i ].position = source.vertices[ i ].position;
|
||||
}
|
||||
node.m_name = source.node.name;
|
||||
return node;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::uint32_t kMagic { MAKE_ID4( 'E', 'U', '7', 'B' ) };
|
||||
constexpr std::uint32_t kVersionV4 { 4 };
|
||||
constexpr std::uint32_t kVersionV5 { 5 };
|
||||
constexpr std::uint32_t kVersionV6 { 6 };
|
||||
constexpr std::uint32_t kVersionV7 { 7 };
|
||||
constexpr std::uint32_t kVersionV8 { 8 };
|
||||
constexpr std::uint32_t kChunkTerr { MAKE_ID4( 'T', 'E', 'R', 'R' ) };
|
||||
|
||||
std::unordered_set<std::string> g_load_session;
|
||||
|
||||
[[nodiscard]] std::string
|
||||
scenery_relative_file( std::string const &resolved ) {
|
||||
if( resolved.starts_with( Global.asCurrentSceneryPath ) ) {
|
||||
return resolved.substr( Global.asCurrentSceneryPath.size() );
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
include_binary_path( Eu7Include const &include ) {
|
||||
if( !include.binary_path.empty() ) {
|
||||
return resolve_scenery_path( include.binary_path );
|
||||
}
|
||||
if( !include.source_path.empty() ) {
|
||||
return binary_path( include.source_path );
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void
|
||||
evict_non_pack_modules_from_cache( std::string const &pack_root_resolved ) {
|
||||
for( auto it { g_module_file_cache.begin() }; it != g_module_file_cache.end(); ) {
|
||||
if( it->first != pack_root_resolved ) {
|
||||
it = g_module_file_cache.erase( it );
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
load_module_recursive(
|
||||
std::string const &path,
|
||||
simulation::state_serializer &serializer,
|
||||
std::unordered_set<std::string> &loaded,
|
||||
Eu7TransformContext const &include_prefix = {},
|
||||
std::vector<std::string> const &include_parameters = {} ) {
|
||||
++load_stats().module_visits;
|
||||
|
||||
auto const resolved { resolve_scenery_path( path ) };
|
||||
auto const load_key { module_load_key( resolved, include_parameters ) };
|
||||
if( loaded.contains( load_key ) ) {
|
||||
++load_stats().module_deduped;
|
||||
return true;
|
||||
}
|
||||
if( false == probe_file( resolved ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Eu7Module const *template_module { nullptr };
|
||||
Eu7Module owned_module;
|
||||
try {
|
||||
auto const cached { g_module_file_cache.find( resolved ) };
|
||||
if( cached != g_module_file_cache.end() ) {
|
||||
++load_stats().module_cache_hit;
|
||||
template_module = &cached->second;
|
||||
}
|
||||
else {
|
||||
ScopedTimer const read_timer { load_stats().read_ms };
|
||||
owned_module = read_module( resolved );
|
||||
++load_stats().module_read;
|
||||
auto const emplaced { g_module_file_cache.emplace( resolved, std::move( owned_module ) ) };
|
||||
template_module = &emplaced.first->second;
|
||||
}
|
||||
}
|
||||
catch( std::exception const &ex ) {
|
||||
ErrorLog( std::string{ "EU7: blad odczytu \"" + resolved + "\": " } + ex.what() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( g_packed_root_active && is_model_only_module( *template_module ) ) {
|
||||
loaded.insert( load_key );
|
||||
++load_stats().pack_skipped_includes;
|
||||
return true;
|
||||
}
|
||||
|
||||
loaded.insert( load_key );
|
||||
++load_stats().module_applied;
|
||||
|
||||
if(
|
||||
false == g_packed_root_active &&
|
||||
include_parameters.empty() &&
|
||||
transform_is_empty( include_prefix ) &&
|
||||
template_module->has_pack_chunk ) {
|
||||
g_packed_root_active = true;
|
||||
}
|
||||
|
||||
std::vector<Eu7Include> fallback_includes;
|
||||
for( auto const &include : template_module->includes ) {
|
||||
auto const child { include_binary_path( include ) };
|
||||
auto const ref {
|
||||
include.source_path.empty() ? include.binary_path : include.source_path };
|
||||
bool child_loaded { false };
|
||||
if( !child.empty() && should_use_binary_module( ref ) ) {
|
||||
child_loaded = load_module_recursive(
|
||||
child,
|
||||
serializer,
|
||||
loaded,
|
||||
include.site_transform,
|
||||
include.parameters );
|
||||
}
|
||||
if( child_loaded ) {
|
||||
continue;
|
||||
}
|
||||
if( !child.empty() && probe_file( child ) && false == should_use_binary_module( ref ) ) {
|
||||
WriteLog(
|
||||
"EU7: przestarzaly .eu7, fallback SCM: " + include_text_path( include ) );
|
||||
}
|
||||
else if( !child.empty() ) {
|
||||
WriteLog(
|
||||
"EU7 include niedostepny, fallback SCM: " + include_text_path( include ) );
|
||||
}
|
||||
fallback_includes.push_back( include );
|
||||
}
|
||||
|
||||
scene::scratch_data scratch;
|
||||
auto const current_relative { scenery_relative_file( resolved ) };
|
||||
if( false == fallback_includes.empty() ) {
|
||||
ScopedTimer const scm_timer { load_stats().scm_fallback_ms };
|
||||
load_stats().scm_fallback += fallback_includes.size();
|
||||
for( auto const &include : fallback_includes ) {
|
||||
serializer.deserialize_include_file(
|
||||
include_text_path( include ),
|
||||
current_relative,
|
||||
include.parameters,
|
||||
scratch );
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
fallback_includes.empty() &&
|
||||
is_model_only_module( *template_module ) ) {
|
||||
++load_stats().model_fast_path;
|
||||
|
||||
std::vector<Eu7Model> models { template_module->scene.models };
|
||||
{
|
||||
ScopedTimer const place_timer { load_stats().place_fast_ms };
|
||||
if( false == include_parameters.empty() ) {
|
||||
apply_include_parameters_to_models( models, include_parameters );
|
||||
}
|
||||
if( false == transform_is_empty( include_prefix ) ) {
|
||||
compose_models_with_prefix( models, include_prefix );
|
||||
}
|
||||
apply_include_placement_to_models(
|
||||
models, template_module->include_placement, include_parameters );
|
||||
}
|
||||
serializer.apply_eu7_models( models, scratch );
|
||||
}
|
||||
else {
|
||||
++load_stats().module_full_path;
|
||||
|
||||
Eu7Module module { *template_module };
|
||||
{
|
||||
ScopedTimer const place_timer { load_stats().place_full_ms };
|
||||
if( false == include_parameters.empty() ) {
|
||||
apply_include_parameters_to_scene( module.scene, include_parameters );
|
||||
}
|
||||
|
||||
if( false == transform_is_empty( include_prefix ) ) {
|
||||
compose_scene_with_include_prefix( module.scene, include_prefix );
|
||||
}
|
||||
|
||||
apply_include_placement_to_scene(
|
||||
module.scene, module.include_placement, include_parameters );
|
||||
}
|
||||
|
||||
apply_module( module, serializer, scratch );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
file_has_terr_chunk( std::string const &path ) {
|
||||
std::ifstream input { path, std::ios::binary };
|
||||
if( !input ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if( sn_utils::ld_uint32( input ) != kMagic ) {
|
||||
return false;
|
||||
}
|
||||
const std::uint32_t version { sn_utils::ld_uint32( input ) };
|
||||
if(
|
||||
version != kVersionV4 && version != kVersionV5 && version != kVersionV6 &&
|
||||
version != kVersionV7 && version != kVersionV8 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while( input.peek() != EOF ) {
|
||||
const std::uint32_t chunk_type { sn_utils::ld_uint32( input ) };
|
||||
const std::uint32_t chunk_size { sn_utils::ld_uint32( input ) };
|
||||
if( chunk_size < 8 ) {
|
||||
return false;
|
||||
}
|
||||
if( chunk_type == kChunkTerr ) {
|
||||
return true;
|
||||
}
|
||||
input.seekg( static_cast<std::streamoff>( chunk_size - 8 ), std::ios::cur );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string
|
||||
resolve_scenery_path( std::string const &reference ) {
|
||||
auto path { reference };
|
||||
while( false == path.empty() && path[ 0 ] == '$' ) {
|
||||
path.erase( 0, 1 );
|
||||
}
|
||||
replace_slashes( path );
|
||||
if( path.starts_with( Global.asCurrentSceneryPath ) ) {
|
||||
return path;
|
||||
}
|
||||
return Global.asCurrentSceneryPath + path;
|
||||
}
|
||||
|
||||
std::string
|
||||
binary_path( std::string const &reference ) {
|
||||
auto path { resolve_scenery_path( reference ) };
|
||||
if( path.ends_with( ".scm" ) || path.ends_with( ".sbt" ) || path.ends_with( ".inc" ) ||
|
||||
path.ends_with( ".scn" ) ) {
|
||||
path.replace( path.size() - 4, 4, ".eu7" );
|
||||
}
|
||||
else if( false == path.ends_with( ".eu7" ) ) {
|
||||
erase_extension( path );
|
||||
path += ".eu7";
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string
|
||||
resolve_parser_include_path(
|
||||
std::string const &parser_path,
|
||||
std::string const ¤t_file,
|
||||
std::string const &include_reference ) {
|
||||
auto reference { include_reference };
|
||||
while( false == reference.empty() && reference[ 0 ] == '$' ) {
|
||||
reference.erase( 0, 1 );
|
||||
}
|
||||
replace_slashes( reference );
|
||||
if( reference.starts_with( Global.asCurrentSceneryPath ) ) {
|
||||
return reference;
|
||||
}
|
||||
if( reference.find( '/' ) != std::string::npos ) {
|
||||
return parser_path + reference;
|
||||
}
|
||||
if( false == current_file.empty() ) {
|
||||
auto const slash { current_file.find_last_of( '/' ) };
|
||||
if( slash != std::string::npos ) {
|
||||
return parser_path + current_file.substr( 0, slash + 1 ) + reference;
|
||||
}
|
||||
}
|
||||
return parser_path + reference;
|
||||
}
|
||||
|
||||
std::string
|
||||
include_eu7_path(
|
||||
std::string const &parser_path,
|
||||
std::string const ¤t_file,
|
||||
std::string const &include_reference ) {
|
||||
return binary_path( resolve_parser_include_path( parser_path, current_file, include_reference ) );
|
||||
}
|
||||
|
||||
std::string
|
||||
terrain_binary_path( std::string const &terrain_reference ) {
|
||||
return binary_path( terrain_reference );
|
||||
}
|
||||
|
||||
bool
|
||||
probe_file( std::string const &path ) {
|
||||
return FileExists( path ) && is_valid_eu7b_file( path );
|
||||
}
|
||||
|
||||
bool
|
||||
probe_baked_scenario( std::string const &scenario_file ) {
|
||||
return probe_file( resolve_scenery_path( binary_path( scenario_file ) ) );
|
||||
}
|
||||
|
||||
bool
|
||||
is_text_module_extension( std::string const &path ) {
|
||||
return path.ends_with( ".scn" ) || path.ends_with( ".scm" ) ||
|
||||
path.ends_with( ".inc" ) || path.ends_with( ".sbt" );
|
||||
}
|
||||
|
||||
std::string
|
||||
text_source_path( std::string const &reference ) {
|
||||
auto const resolved { resolve_scenery_path( reference ) };
|
||||
if( is_text_module_extension( resolved ) ) {
|
||||
return resolved;
|
||||
}
|
||||
if( resolved.ends_with( ".eu7" ) ) {
|
||||
auto stem { resolved };
|
||||
stem.erase( stem.size() - 4 );
|
||||
for( auto const *ext : { ".scm", ".scn", ".inc", ".sbt" } ) {
|
||||
auto const candidate { stem + ext };
|
||||
if( FileExists( candidate ) ) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool
|
||||
text_module_is_newer_than_binary( std::string const &reference ) {
|
||||
auto const text { text_source_path( reference ) };
|
||||
if( text.empty() || false == FileExists( text ) ) {
|
||||
return false;
|
||||
}
|
||||
auto const eu7 { binary_path( reference ) };
|
||||
if( false == FileExists( eu7 ) ) {
|
||||
return true;
|
||||
}
|
||||
return last_modified( text ) > last_modified( eu7 );
|
||||
}
|
||||
|
||||
bool
|
||||
should_use_binary_module( std::string const &reference ) {
|
||||
auto const eu7 { binary_path( reference ) };
|
||||
return probe_file( eu7 ) && false == text_module_is_newer_than_binary( reference );
|
||||
}
|
||||
|
||||
bool
|
||||
probe_terrain_file( std::string const &path ) {
|
||||
return FileExists( path ) && file_has_terr_chunk( path );
|
||||
}
|
||||
|
||||
bool
|
||||
is_scenario_terrain( std::string const &scenario_file ) {
|
||||
auto stem { scenario_file };
|
||||
while( false == stem.empty() && stem[ 0 ] == '$' ) {
|
||||
stem.erase( 0, 1 );
|
||||
}
|
||||
erase_extension( stem );
|
||||
return probe_terrain_file( resolve_scenery_path( stem + ".eu7" ) );
|
||||
}
|
||||
|
||||
bool
|
||||
try_load_scenario_terrain( basic_region ®ion, std::string const &scenario_file ) {
|
||||
auto stem { scenario_file };
|
||||
while( false == stem.empty() && stem[ 0 ] == '$' ) {
|
||||
stem.erase( 0, 1 );
|
||||
}
|
||||
erase_extension( stem );
|
||||
return load_terrain( region, resolve_scenery_path( stem + ".eu7" ) );
|
||||
}
|
||||
|
||||
void
|
||||
insert_terrain_shapes( basic_region ®ion, Eu7Module const &module ) {
|
||||
scene::scratch_data scratch;
|
||||
std::size_t shape_count { 0 };
|
||||
std::size_t vertex_count { 0 };
|
||||
for( auto const &source : module.scene.terrain_shapes ) {
|
||||
shape_node shape { build_shape_node( source ) };
|
||||
vertex_count += source.vertices.size();
|
||||
region.insert( shape, scratch, false );
|
||||
++shape_count;
|
||||
}
|
||||
if( shape_count > 0 ) {
|
||||
WriteLog(
|
||||
"EU7 terrain shapes: count=" + std::to_string( shape_count ) +
|
||||
" vertices=" + std::to_string( vertex_count ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
load_terrain( basic_region ®ion, std::string const &path ) {
|
||||
if( false == probe_terrain_file( path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
auto const module { read_module( path ) };
|
||||
if( module.scene.terrain_shapes.empty() ) {
|
||||
ErrorLog( "EU7: brak chunka TERR w \"" + path + "\"" );
|
||||
return false;
|
||||
}
|
||||
insert_terrain_shapes( region, module );
|
||||
WriteLog( "EU7 terrain loaded: " + path );
|
||||
return true;
|
||||
}
|
||||
catch( std::exception const &ex ) {
|
||||
ErrorLog( std::string{ "EU7: blad terenu \"" + path + "\": " } + ex.what() );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
begin_load_session() {
|
||||
g_load_session.clear();
|
||||
g_packed_root_active = false;
|
||||
reset_section_stream();
|
||||
g_module_file_cache.clear();
|
||||
reset_load_stats();
|
||||
reset_pack_bench();
|
||||
}
|
||||
|
||||
bool
|
||||
is_module_loaded( std::string const &path ) {
|
||||
return g_load_session.contains( resolve_scenery_path( path ) );
|
||||
}
|
||||
|
||||
bool
|
||||
pack_scenery_active() {
|
||||
return g_packed_root_active;
|
||||
}
|
||||
|
||||
bool
|
||||
load_module( std::string const &path, simulation::state_serializer &serializer ) {
|
||||
if( simulation::Region == nullptr ) {
|
||||
ErrorLog( "EU7: Region nie jest zainicjalizowany" );
|
||||
return false;
|
||||
}
|
||||
auto const resolved { resolve_scenery_path( path ) };
|
||||
auto const ok { load_module_recursive( path, serializer, g_load_session ) };
|
||||
if( ok ) {
|
||||
if( auto const cached { g_module_file_cache.find( resolved ) };
|
||||
cached != g_module_file_cache.end() && cached->second.has_pack_chunk ) {
|
||||
init_section_stream( cached->second, resolved, serializer );
|
||||
prime_section_stream( cached->second );
|
||||
evict_non_pack_modules_from_cache( resolved );
|
||||
}
|
||||
else {
|
||||
evict_non_pack_modules_from_cache( {} );
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
} // namespace scene::eu7
|
||||
123
scene/eu7/eu7_loader.h
Normal file
123
scene/eu7/eu7_loader.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/eu7/eu7_types.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace simulation {
|
||||
class state_serializer;
|
||||
}
|
||||
|
||||
namespace scene {
|
||||
|
||||
class basic_region;
|
||||
class shape_node;
|
||||
class lines_node;
|
||||
|
||||
namespace eu7 {
|
||||
|
||||
// Buduje shape_node z rekordu EU7B (friend shape_node).
|
||||
[[nodiscard]] shape_node
|
||||
build_shape_node( Eu7Shape const &Source );
|
||||
|
||||
// Buduje lines_node z rekordu EU7B (friend lines_node).
|
||||
[[nodiscard]] lines_node
|
||||
build_lines_node( Eu7Lines const &Source );
|
||||
|
||||
// Pełna ścieżka względem Global.asCurrentSceneryPath (jak basic_region::deserialize).
|
||||
[[nodiscard]] std::string
|
||||
resolve_scenery_path( std::string const &Reference );
|
||||
|
||||
// Zamienia .scm/.sbt/.inc/.scn na .eu7 przy tym samym stemie.
|
||||
[[nodiscard]] std::string
|
||||
binary_path( std::string const &Reference );
|
||||
|
||||
// Jak cParser: mPath + include, z katalogiem pliku biezacego dla sciezek wzglednych.
|
||||
[[nodiscard]] std::string
|
||||
resolve_parser_include_path(
|
||||
std::string const &ParserPath,
|
||||
std::string const &CurrentFile,
|
||||
std::string const &IncludeReference );
|
||||
|
||||
// Sciezka .eu7 obok include (ta sama lokalizacja co SCM/INC).
|
||||
[[nodiscard]] std::string
|
||||
include_eu7_path(
|
||||
std::string const &ParserPath,
|
||||
std::string const &CurrentFile,
|
||||
std::string const &IncludeReference );
|
||||
|
||||
// Alias dla terenu (kompatybilnosc wsteczna).
|
||||
[[nodiscard]] std::string
|
||||
terrain_binary_path( std::string const &TerrainReference );
|
||||
|
||||
// Czy plik istnieje i jest poprawnym EU7B (v4-v8).
|
||||
[[nodiscard]] bool
|
||||
probe_file( std::string const &Path );
|
||||
|
||||
// Czy obok scenariusza (.scn/.scm) jest zbakowany plik <stem>.eu7.
|
||||
[[nodiscard]] bool
|
||||
probe_baked_scenario( std::string const &ScenarioFile );
|
||||
|
||||
// Rozszerzenia modulow tekstowych (.scn/.scm/.inc/.sbt).
|
||||
[[nodiscard]] bool
|
||||
is_text_module_extension( std::string const &Path );
|
||||
|
||||
// Dla .eu7: istniejacy plik tekstowy obok; dla referencji tekstowej: sciezka rozwiazana.
|
||||
[[nodiscard]] std::string
|
||||
text_source_path( std::string const &Reference );
|
||||
|
||||
// Tekst istnieje i jest nowszy niz .eu7 (lub brak .eu7).
|
||||
[[nodiscard]] bool
|
||||
text_module_is_newer_than_binary( std::string const &Reference );
|
||||
|
||||
// Istniejacy, poprawny .eu7 nie starszy niz odpowiednik tekstowy.
|
||||
[[nodiscard]] bool
|
||||
should_use_binary_module( std::string const &Reference );
|
||||
|
||||
// Czy plik zawiera chunk TERR.
|
||||
[[nodiscard]] bool
|
||||
probe_terrain_file( std::string const &Path );
|
||||
|
||||
// Czy obok scenariusza jest <stem>.eu7 z terenem (analogia do .sbt).
|
||||
[[nodiscard]] bool
|
||||
is_scenario_terrain( std::string const &ScenarioFile );
|
||||
|
||||
// Ładuje <stem>.eu7 dla danego scenariusza. Zwraca false gdy brak pliku.
|
||||
[[nodiscard]] bool
|
||||
try_load_scenario_terrain( basic_region &Region, std::string const &ScenarioFile );
|
||||
|
||||
// Wczytuje chunk TERR z podanej ścieżki .eu7 do regionu (szybka ścieżka).
|
||||
[[nodiscard]] bool
|
||||
load_terrain( basic_region &Region, std::string const &Path );
|
||||
|
||||
// Wstawia kształty terenu z Eu7Module do regionu.
|
||||
void
|
||||
insert_terrain_shapes( basic_region &Region, Eu7Module const &Module );
|
||||
|
||||
// Czyści sesje deduplikacji przed ladowaniem scenariusza EU7.
|
||||
void
|
||||
begin_load_session();
|
||||
|
||||
// Czy modul juz zostal zaladowany w biezacej sesji (parser include nie laduje drugi raz).
|
||||
[[nodiscard]] bool
|
||||
is_module_loaded( std::string const &Path );
|
||||
|
||||
// Pełne ładowanie modułu EU7B (wszystkie chunki oprócz STRS).
|
||||
[[nodiscard]] bool
|
||||
load_module( std::string const &Path, simulation::state_serializer &Serializer );
|
||||
|
||||
// Root scenariusza ma chunk PACK — modele scenerii ida ze streamingu, nie z MODL w .inc.
|
||||
[[nodiscard]] bool
|
||||
pack_scenery_active();
|
||||
|
||||
} // namespace eu7
|
||||
} // namespace scene
|
||||
135
scene/eu7/eu7_model_prefetch.cpp
Normal file
135
scene/eu7/eu7_model_prefetch.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "scene/eu7/eu7_model_prefetch.h"
|
||||
|
||||
#include "model/AnimModel.h"
|
||||
#include "model/MdlMngr.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include "scene/eu7/eu7_pack_bench.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace scene::eu7 {
|
||||
namespace {
|
||||
|
||||
std::mutex g_pack_mesh_load_mutex;
|
||||
std::unordered_set<std::string> g_session_warmed_textures;
|
||||
|
||||
[[nodiscard]] bool
|
||||
pack_texture_usable( std::string texture_file ) {
|
||||
if( texture_file.empty() || texture_file == "none" || texture_file.front() == '*' ) {
|
||||
return false;
|
||||
}
|
||||
replace_slashes( texture_file );
|
||||
if( texture_file == "none" || texture_file.ends_with( "/none" ) ) {
|
||||
return false;
|
||||
}
|
||||
if( texture_file.find( "tr/none" ) != std::string::npos ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
preload_pack_model_file( std::string const &model_file ) {
|
||||
if( false == TModelsManager::IsModelCached( model_file ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( auto *const mesh { TModelsManager::GetModel( model_file, false, false ) } ) {
|
||||
TAnimModel::warm_instanceable_cache( mesh );
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
warm_one_pack_texture( std::string texture_file, std::unordered_set<std::string> &seen ) {
|
||||
if( false == pack_texture_usable( texture_file ) ) {
|
||||
return false;
|
||||
}
|
||||
replace_slashes( texture_file );
|
||||
if( false == seen.insert( texture_file ).second ) {
|
||||
return false;
|
||||
}
|
||||
if( false == g_session_warmed_textures.insert( texture_file ).second ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const resolved { TextureTest( ToLower( texture_file ) ) };
|
||||
if( resolved.empty() ) {
|
||||
pack_bench_inc( &Eu7PackBench::main_texture_warm_miss );
|
||||
log_pack_texture_fail( texture_file );
|
||||
return false;
|
||||
}
|
||||
GfxRenderer->Fetch_Material( resolved );
|
||||
|
||||
for( int skinindex { 1 }; skinindex <= 4; ++skinindex ) {
|
||||
auto const multi {
|
||||
TextureTest( ToLower( texture_file + "," + std::to_string( skinindex ) ) ) };
|
||||
if( multi.empty() ) {
|
||||
break;
|
||||
}
|
||||
GfxRenderer->Fetch_Material( multi );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
reset_pack_texture_warm_cache() {
|
||||
g_session_warmed_textures.clear();
|
||||
}
|
||||
|
||||
void
|
||||
preload_pack_models( std::vector<Eu7Model> const &models ) {
|
||||
std::unordered_set<std::string> seen;
|
||||
seen.reserve( models.size() );
|
||||
|
||||
for( auto const &model : models ) {
|
||||
auto model_file { model.model_file };
|
||||
if( model_file.empty() || model_file == "notload" ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
replace_slashes( model_file );
|
||||
if( false == seen.insert( model_file ).second ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock { g_pack_mesh_load_mutex };
|
||||
preload_pack_model_file( model_file );
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t
|
||||
warm_pack_textures_main( Eu7Model const *const models, std::size_t const count ) {
|
||||
if( models == nullptr || count == 0 || GfxRenderer == nullptr ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> seen;
|
||||
seen.reserve( std::min( count, std::size_t { 64 } ) );
|
||||
std::size_t warmed { 0 };
|
||||
|
||||
for( std::size_t i { 0 }; i < count; ++i ) {
|
||||
if( warm_one_pack_texture( models[ i ].texture_file, seen ) ) {
|
||||
++warmed;
|
||||
}
|
||||
}
|
||||
return warmed;
|
||||
}
|
||||
|
||||
} // namespace scene::eu7
|
||||
33
scene/eu7/eu7_model_prefetch.h
Normal file
33
scene/eu7/eu7_model_prefetch.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/eu7/eu7_types.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
// Tylko warm_instanceable_cache dla juz zcache'owanych meshy (bez cold GetModel na workerze).
|
||||
void
|
||||
preload_pack_models( std::vector<Eu7Model> const &Models );
|
||||
|
||||
// Main thread: Fetch_Material dla unikalnych texture_file z chunka przed apply.
|
||||
// Zwraca liczbe unikalnych Fetch_Material w tym slice.
|
||||
std::size_t
|
||||
warm_pack_textures_main( Eu7Model const *models, std::size_t count );
|
||||
|
||||
void
|
||||
reset_pack_texture_warm_cache();
|
||||
|
||||
} // namespace scene::eu7
|
||||
405
scene/eu7/eu7_pack_bench.cpp
Normal file
405
scene/eu7/eu7_pack_bench.cpp
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "scene/eu7/eu7_pack_bench.h"
|
||||
|
||||
#include "utilities/Logs.h"
|
||||
#include "utilities/utilities.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace scene::eu7 {
|
||||
namespace {
|
||||
|
||||
Eu7PackBench g_pack_bench;
|
||||
Eu7PackBench g_pack_bench_stream;
|
||||
Eu7PackBench g_pack_bench_bootstrap;
|
||||
bool g_stream_phase { false };
|
||||
std::unordered_set<std::string> g_logged_texture_fails;
|
||||
|
||||
struct BenchLine {
|
||||
std::string label;
|
||||
double ms { 0.0 };
|
||||
std::string detail;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::string
|
||||
format_seconds( double const ms ) {
|
||||
std::ostringstream out;
|
||||
out << std::fixed << std::setprecision( 1 ) << ( ms / 1000.0 ) << 's';
|
||||
return out.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
format_ms_per( double const ms, std::uint64_t const count ) {
|
||||
if( count == 0 ) {
|
||||
return "n/a";
|
||||
}
|
||||
std::ostringstream out;
|
||||
out << std::fixed << std::setprecision( 2 ) << ( ms / static_cast<double>( count ) ) << " ms/item";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void
|
||||
note_peak(
|
||||
std::uint64_t Eu7PackBench::*const field,
|
||||
std::size_t const size ) {
|
||||
auto &total_peak { g_pack_bench.*field };
|
||||
total_peak = std::max( total_peak, size );
|
||||
if( g_stream_phase ) {
|
||||
auto &stream_peak { g_pack_bench_stream.*field };
|
||||
stream_peak = std::max( stream_peak, size );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
add_bench_ms( double Eu7PackBench::*const field, double const delta ) {
|
||||
g_pack_bench.*field += delta;
|
||||
if( g_stream_phase ) {
|
||||
g_pack_bench_stream.*field += delta;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
log_pack_bench_impl( Eu7PackBench const &bench, char const *const title ) {
|
||||
auto const total { bench.accounted_ms() };
|
||||
|
||||
if( total <= 0.0 && bench.main_drain_calls == 0 && bench.worker_sections_done == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const main_apply_sum {
|
||||
bench.main_load_eu7_pack_ms + bench.main_load_eu7_full_ms + bench.main_region_insert_ms };
|
||||
auto const chunk_sum {
|
||||
bench.main_chunk_cold_ms + bench.main_chunk_warm_tex_ms + bench.main_chunk_pointer_apply_ms };
|
||||
|
||||
std::vector<BenchLine> lines {
|
||||
{ "worker read_pack", bench.worker_read_pack_ms,
|
||||
"sections=" + std::to_string( bench.worker_sections_done ) +
|
||||
" finalized=" + std::to_string( bench.sections_finalized ) +
|
||||
" models=" + std::to_string( bench.worker_models_decoded ) +
|
||||
" fail=" + std::to_string( bench.worker_failures ) },
|
||||
{ "main read_pack", bench.main_read_pack_ms,
|
||||
"models=" + std::to_string( bench.worker_models_decoded ) },
|
||||
{ "main GetModel (cold)", bench.main_cold_getmodel_ms,
|
||||
"calls=" + std::to_string( bench.main_cold_getmodel_calls ) + " " +
|
||||
format_ms_per( bench.main_cold_getmodel_ms, bench.main_cold_getmodel_calls ) },
|
||||
{ "main LoadEu7Pack", bench.main_load_eu7_pack_ms,
|
||||
"loads=" + std::to_string( bench.main_pack_fast_loads ) + " " +
|
||||
format_ms_per( bench.main_load_eu7_pack_ms, bench.main_pack_fast_loads ) },
|
||||
{ "main LoadEu7 (full)", bench.main_load_eu7_full_ms,
|
||||
"loads=" + std::to_string( bench.main_pack_full_loads ) + " " +
|
||||
format_ms_per( bench.main_load_eu7_full_ms, bench.main_pack_full_loads ) },
|
||||
{ "main Region::insert", bench.main_region_insert_ms,
|
||||
"inserts=" + std::to_string( bench.main_region_inserts ) + " " +
|
||||
format_ms_per( bench.main_region_insert_ms, bench.main_region_inserts ) },
|
||||
{ "main apply (load+region)", main_apply_sum,
|
||||
"instances=" + std::to_string( bench.main_instances_applied ) + " " +
|
||||
format_ms_per( main_apply_sum, bench.main_instances_applied ) },
|
||||
{ "chunk cold preload", bench.main_chunk_cold_ms,
|
||||
"loads=" + std::to_string( bench.main_chunk_cold_loads ) + " " +
|
||||
format_ms_per( bench.main_chunk_cold_ms, bench.main_chunk_cold_loads ) },
|
||||
{ "chunk warm textures", bench.main_chunk_warm_tex_ms,
|
||||
"fetches=" + std::to_string( bench.main_chunk_tex_fetches ) + " " +
|
||||
format_ms_per( bench.main_chunk_warm_tex_ms, bench.main_chunk_tex_fetches ) },
|
||||
{ "chunk pointer apply", bench.main_chunk_pointer_apply_ms,
|
||||
"chunks=" + std::to_string( bench.main_chunks ) + " inst=" +
|
||||
std::to_string( bench.main_chunk_instances ) + " " +
|
||||
format_ms_per( bench.main_chunk_pointer_apply_ms, bench.main_chunk_instances ) },
|
||||
{ "chunk wall (phases)", chunk_sum,
|
||||
"chunks=" + std::to_string( bench.main_chunks ) + " slow8=" +
|
||||
std::to_string( bench.chunk_slow_8ms ) + " slow16=" +
|
||||
std::to_string( bench.chunk_slow_16ms ) },
|
||||
};
|
||||
|
||||
std::sort(
|
||||
lines.begin(),
|
||||
lines.end(),
|
||||
[]( BenchLine const &lhs, BenchLine const &rhs ) {
|
||||
return lhs.ms > rhs.ms;
|
||||
} );
|
||||
|
||||
WriteLog( std::string{ title } + " (accounted " + format_seconds( total ) + "):" );
|
||||
for( auto const &line : lines ) {
|
||||
if( line.ms <= 0.0 ) {
|
||||
continue;
|
||||
}
|
||||
auto const pct {
|
||||
total > 0.0 ?
|
||||
( 100.0 * line.ms / total ) :
|
||||
0.0 };
|
||||
std::ostringstream row;
|
||||
row << std::fixed << std::setprecision( 1 );
|
||||
row << " " << line.label << ": " << format_seconds( line.ms );
|
||||
if( total > 0.0 ) {
|
||||
row << " (" << pct << "%)";
|
||||
}
|
||||
if( false == line.detail.empty() ) {
|
||||
row << " " << line.detail;
|
||||
}
|
||||
WriteLog( row.str() );
|
||||
}
|
||||
|
||||
WriteLog(
|
||||
" drain: calls=" + std::to_string( bench.main_drain_calls ) +
|
||||
" wait_cold_frames=" + std::to_string( bench.main_drain_wait_cold_mesh ) +
|
||||
" wait_worker_frames=" + std::to_string( bench.main_drain_wait_worker ) +
|
||||
" idle_frames=" + std::to_string( bench.main_drain_idle ) );
|
||||
WriteLog(
|
||||
" stream: reenqueue=" + std::to_string( bench.stream_reenqueue ) +
|
||||
" lookahead=" + std::to_string( bench.stream_lookahead_enqueue ) +
|
||||
" tex_fail=" + std::to_string( bench.main_texture_assign_fail ) +
|
||||
" warm_miss=" + std::to_string( bench.main_texture_warm_miss ) );
|
||||
WriteLog(
|
||||
" peaks: ready_q=" + std::to_string( bench.peak_ready_queue ) +
|
||||
" in_flight=" + std::to_string( bench.peak_in_flight ) +
|
||||
" cold_q=" + std::to_string( bench.peak_pending_cold_meshes ) );
|
||||
if( bench.main_chunks > 0 ) {
|
||||
WriteLog(
|
||||
" chunk peaks: wall_ms=" + std::to_string( static_cast<int>( bench.peak_chunk_ms ) ) +
|
||||
" inst=" + std::to_string( bench.peak_chunk_instances ) +
|
||||
" cold=" + std::to_string( bench.peak_chunk_cold ) +
|
||||
" last_ms=" + std::to_string( static_cast<int>( bench.last_chunk_ms ) ) +
|
||||
" last_inst=" + std::to_string( bench.last_chunk_instances ) +
|
||||
" budget_stops=" + std::to_string( bench.drain_budget_stops ) );
|
||||
}
|
||||
|
||||
if( false == lines.empty() && lines.front().ms > 0.0 ) {
|
||||
WriteLog( " >> bottleneck: " + lines.front().label );
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
double
|
||||
Eu7PackBench::accounted_ms() const {
|
||||
return (
|
||||
worker_read_pack_ms +
|
||||
main_read_pack_ms +
|
||||
main_cold_getmodel_ms +
|
||||
main_load_eu7_pack_ms +
|
||||
main_load_eu7_full_ms +
|
||||
main_region_insert_ms );
|
||||
}
|
||||
|
||||
double
|
||||
Eu7PackBench::chunk_accounted_ms() const {
|
||||
return (
|
||||
main_chunk_cold_ms +
|
||||
main_chunk_warm_tex_ms +
|
||||
main_chunk_pointer_apply_ms );
|
||||
}
|
||||
|
||||
PackBenchTimer::PackBenchTimer( double Eu7PackBench::*const field )
|
||||
: m_field { field }
|
||||
, m_start { std::chrono::steady_clock::now() } {}
|
||||
|
||||
PackBenchTimer::~PackBenchTimer() {
|
||||
auto const end { std::chrono::steady_clock::now() };
|
||||
auto const delta {
|
||||
std::chrono::duration<double, std::milli>( end - m_start ).count() };
|
||||
g_pack_bench.*m_field += delta;
|
||||
if( g_stream_phase ) {
|
||||
g_pack_bench_stream.*m_field += delta;
|
||||
}
|
||||
}
|
||||
|
||||
Eu7PackBench &
|
||||
pack_bench() {
|
||||
return g_pack_bench;
|
||||
}
|
||||
|
||||
Eu7PackBench &
|
||||
pack_bench_stream() {
|
||||
return g_pack_bench_stream;
|
||||
}
|
||||
|
||||
Eu7PackBench const &
|
||||
pack_bench_bootstrap() {
|
||||
return g_pack_bench_bootstrap;
|
||||
}
|
||||
|
||||
bool
|
||||
pack_bench_stream_phase_active() {
|
||||
return g_stream_phase;
|
||||
}
|
||||
|
||||
void
|
||||
reset_pack_bench() {
|
||||
g_pack_bench = {};
|
||||
g_pack_bench_stream = {};
|
||||
g_pack_bench_bootstrap = {};
|
||||
g_stream_phase = false;
|
||||
g_logged_texture_fails.clear();
|
||||
}
|
||||
|
||||
void
|
||||
pack_bench_begin_stream_phase() {
|
||||
g_pack_bench_bootstrap = g_pack_bench;
|
||||
g_pack_bench_stream = {};
|
||||
g_stream_phase = true;
|
||||
}
|
||||
|
||||
void
|
||||
pack_bench_note_ready_queue( std::size_t const size ) {
|
||||
note_peak( &Eu7PackBench::peak_ready_queue, size );
|
||||
}
|
||||
|
||||
void
|
||||
pack_bench_note_in_flight( std::size_t const size ) {
|
||||
note_peak( &Eu7PackBench::peak_in_flight, size );
|
||||
}
|
||||
|
||||
void
|
||||
pack_bench_note_pending_cold_meshes( std::size_t const size ) {
|
||||
note_peak( &Eu7PackBench::peak_pending_cold_meshes, size );
|
||||
}
|
||||
|
||||
void
|
||||
pack_bench_inc( std::uint64_t Eu7PackBench::*const field, std::uint64_t const amount ) {
|
||||
g_pack_bench.*field += amount;
|
||||
if( g_stream_phase ) {
|
||||
g_pack_bench_stream.*field += amount;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pack_bench_note_chunk(
|
||||
double const wall_ms,
|
||||
std::size_t const instances,
|
||||
std::size_t const cold_meshes,
|
||||
double const cold_ms,
|
||||
double const warm_tex_ms,
|
||||
double const pointer_apply_ms ) {
|
||||
add_bench_ms( &Eu7PackBench::main_chunk_cold_ms, cold_ms );
|
||||
add_bench_ms( &Eu7PackBench::main_chunk_warm_tex_ms, warm_tex_ms );
|
||||
add_bench_ms( &Eu7PackBench::main_chunk_pointer_apply_ms, pointer_apply_ms );
|
||||
pack_bench_inc( &Eu7PackBench::main_chunks );
|
||||
pack_bench_inc( &Eu7PackBench::main_chunk_instances, instances );
|
||||
pack_bench_inc( &Eu7PackBench::main_chunk_cold_loads, cold_meshes );
|
||||
|
||||
g_pack_bench.last_chunk_ms = wall_ms;
|
||||
g_pack_bench.last_chunk_instances = instances;
|
||||
g_pack_bench.last_chunk_cold = cold_meshes;
|
||||
if( wall_ms > g_pack_bench.peak_chunk_ms ) {
|
||||
g_pack_bench.peak_chunk_ms = wall_ms;
|
||||
g_pack_bench.peak_chunk_instances = instances;
|
||||
g_pack_bench.peak_chunk_cold = cold_meshes;
|
||||
}
|
||||
if( wall_ms >= 8.0 ) {
|
||||
pack_bench_inc( &Eu7PackBench::chunk_slow_8ms );
|
||||
}
|
||||
if( wall_ms >= 16.0 ) {
|
||||
pack_bench_inc( &Eu7PackBench::chunk_slow_16ms );
|
||||
}
|
||||
|
||||
if( false == g_stream_phase ) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &stream { g_pack_bench_stream };
|
||||
stream.last_chunk_ms = wall_ms;
|
||||
stream.last_chunk_instances = instances;
|
||||
stream.last_chunk_cold = cold_meshes;
|
||||
if( wall_ms > stream.peak_chunk_ms ) {
|
||||
stream.peak_chunk_ms = wall_ms;
|
||||
stream.peak_chunk_instances = instances;
|
||||
stream.peak_chunk_cold = cold_meshes;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
log_pack_texture_fail( std::string const &texture_path ) {
|
||||
if( texture_path.empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto key { texture_path };
|
||||
replace_slashes( key );
|
||||
key = ToLower( key );
|
||||
if( false == g_logged_texture_fails.insert( key ).second ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WriteLog( "EU7 PACK tex_fail: \"" + texture_path + "\"" );
|
||||
}
|
||||
|
||||
void
|
||||
log_pack_bench() {
|
||||
if( g_pack_bench_bootstrap.worker_sections_done > 0
|
||||
|| g_pack_bench_bootstrap.accounted_ms() > 0.0 ) {
|
||||
log_pack_bench_impl( g_pack_bench_bootstrap, "EU7 PACK bench [bootstrap]" );
|
||||
return;
|
||||
}
|
||||
log_pack_bench_impl( g_pack_bench, "EU7 PACK bench" );
|
||||
}
|
||||
|
||||
void
|
||||
log_pack_stream_bench() {
|
||||
log_pack_bench_impl( g_pack_bench_stream, "EU7 PACK bench [stream]" );
|
||||
}
|
||||
|
||||
void
|
||||
flush_pack_stream_bench() {
|
||||
if( false == g_stream_phase ) {
|
||||
return;
|
||||
}
|
||||
if(
|
||||
g_pack_bench_stream.sections_finalized == 0 &&
|
||||
g_pack_bench_stream.main_drain_calls == 0 &&
|
||||
g_pack_bench_stream.accounted_ms() <= 0.0 ) {
|
||||
return;
|
||||
}
|
||||
log_pack_stream_bench();
|
||||
}
|
||||
|
||||
void
|
||||
log_pack_stream_status(
|
||||
glm::dvec3 const &camera_position,
|
||||
double const camera_speed_mps,
|
||||
float const inner_ring_progress,
|
||||
float const outer_ring_progress,
|
||||
std::size_t const ready_queue,
|
||||
std::size_t const in_flight,
|
||||
std::size_t const pending_offset,
|
||||
std::size_t const pending_total,
|
||||
double const drain_budget_ms,
|
||||
std::size_t const chunk_limit ) {
|
||||
auto const &bench { g_pack_bench_stream };
|
||||
WriteLog(
|
||||
"EU7 PACK [live]: cam=" + std::to_string( camera_position.x ) + "," +
|
||||
std::to_string( camera_position.z ) + " m/s=" + std::to_string( camera_speed_mps ) +
|
||||
" ring4=" + std::to_string( static_cast<int>( inner_ring_progress * 100.f ) ) + "%" +
|
||||
" ring11=" + std::to_string( static_cast<int>( outer_ring_progress * 100.f ) ) + "%" +
|
||||
" ready=" + std::to_string( ready_queue ) +
|
||||
" in_flight=" + std::to_string( in_flight ) +
|
||||
" pending=" + std::to_string( pending_offset ) + "/" + std::to_string( pending_total ) +
|
||||
" slice=" + std::to_string( chunk_limit ) +
|
||||
" budget_ms=" + std::to_string( static_cast<int>( drain_budget_ms ) ) +
|
||||
" applied=" + std::to_string( bench.main_instances_applied ) +
|
||||
" tex_fail=" + std::to_string( bench.main_texture_assign_fail ) +
|
||||
" warm_miss=" + std::to_string( bench.main_texture_warm_miss ) +
|
||||
" chunks=" + std::to_string( bench.main_chunks ) +
|
||||
" last_chunk_ms=" + std::to_string( static_cast<int>( bench.last_chunk_ms ) ) +
|
||||
" peak_chunk_ms=" + std::to_string( static_cast<int>( bench.peak_chunk_ms ) ) +
|
||||
" slow8=" + std::to_string( bench.chunk_slow_8ms ) +
|
||||
" cold_ms=" + std::to_string( static_cast<int>( bench.main_chunk_cold_ms ) ) +
|
||||
" warm_ms=" + std::to_string( static_cast<int>( bench.main_chunk_warm_tex_ms ) ) +
|
||||
" apply_ms=" + std::to_string( static_cast<int>( bench.main_chunk_pointer_apply_ms ) ) +
|
||||
" reenq=" + std::to_string( bench.stream_reenqueue ) +
|
||||
" ahead=" + std::to_string( bench.stream_lookahead_enqueue ) );
|
||||
}
|
||||
|
||||
} // namespace scene::eu7
|
||||
159
scene/eu7/eu7_pack_bench.h
Normal file
159
scene/eu7/eu7_pack_bench.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
// Liczniki i czasy PACK — osobno od Eu7LoadStats (deserialize / SCM).
|
||||
struct Eu7PackBench {
|
||||
// --- worker (async read PACK) ---
|
||||
double worker_read_pack_ms { 0.0 };
|
||||
std::uint64_t worker_sections_done { 0 };
|
||||
std::uint64_t sections_finalized { 0 };
|
||||
std::uint64_t worker_models_decoded { 0 };
|
||||
std::uint64_t worker_failures { 0 };
|
||||
|
||||
// --- main thread: apply ---
|
||||
double main_read_pack_ms { 0.0 };
|
||||
double main_cold_getmodel_ms { 0.0 };
|
||||
double main_load_eu7_pack_ms { 0.0 };
|
||||
double main_load_eu7_full_ms { 0.0 };
|
||||
double main_region_insert_ms { 0.0 };
|
||||
|
||||
std::uint64_t main_drain_calls { 0 };
|
||||
std::uint64_t main_cold_getmodel_calls { 0 };
|
||||
std::uint64_t main_instances_applied { 0 };
|
||||
std::uint64_t main_pack_fast_loads { 0 };
|
||||
std::uint64_t main_pack_full_loads { 0 };
|
||||
std::uint64_t main_region_inserts { 0 };
|
||||
|
||||
std::uint64_t main_drain_wait_cold_mesh { 0 };
|
||||
std::uint64_t main_drain_wait_worker { 0 };
|
||||
std::uint64_t main_drain_idle { 0 };
|
||||
|
||||
std::uint64_t peak_ready_queue { 0 };
|
||||
std::uint64_t peak_in_flight { 0 };
|
||||
std::uint64_t peak_pending_cold_meshes { 0 };
|
||||
|
||||
std::uint64_t main_texture_assign_fail { 0 };
|
||||
std::uint64_t main_texture_warm_miss { 0 };
|
||||
std::uint64_t stream_reenqueue { 0 };
|
||||
std::uint64_t stream_lookahead_enqueue { 0 };
|
||||
|
||||
// --- main: pointer chunk apply (diag zwiechy) ---
|
||||
double main_chunk_cold_ms { 0.0 };
|
||||
double main_chunk_warm_tex_ms { 0.0 };
|
||||
double main_chunk_pointer_apply_ms { 0.0 };
|
||||
std::uint64_t main_chunks { 0 };
|
||||
std::uint64_t main_chunk_instances { 0 };
|
||||
std::uint64_t main_chunk_cold_loads { 0 };
|
||||
std::uint64_t main_chunk_tex_fetches { 0 };
|
||||
std::uint64_t chunk_slow_8ms { 0 };
|
||||
std::uint64_t chunk_slow_16ms { 0 };
|
||||
std::uint64_t drain_budget_stops { 0 };
|
||||
double peak_chunk_ms { 0.0 };
|
||||
std::uint64_t peak_chunk_instances { 0 };
|
||||
std::uint64_t peak_chunk_cold { 0 };
|
||||
double last_chunk_ms { 0.0 };
|
||||
std::uint64_t last_chunk_instances { 0 };
|
||||
std::uint64_t last_chunk_cold { 0 };
|
||||
|
||||
[[nodiscard]] double accounted_ms() const;
|
||||
[[nodiscard]] double chunk_accounted_ms() const;
|
||||
};
|
||||
|
||||
class PackBenchTimer {
|
||||
public:
|
||||
explicit PackBenchTimer( double Eu7PackBench::*Field );
|
||||
~PackBenchTimer();
|
||||
|
||||
PackBenchTimer( PackBenchTimer const & ) = delete;
|
||||
PackBenchTimer &operator=( PackBenchTimer const & ) = delete;
|
||||
|
||||
private:
|
||||
double Eu7PackBench::*const m_field;
|
||||
std::chrono::steady_clock::time_point m_start;
|
||||
};
|
||||
|
||||
[[nodiscard]] Eu7PackBench &
|
||||
pack_bench();
|
||||
|
||||
// Po dismiss overlay — liczniki runtime streamingu (latanie kamera / jazda).
|
||||
[[nodiscard]] Eu7PackBench &
|
||||
pack_bench_stream();
|
||||
|
||||
// Snapshot z fazy bootstrap (ekran ladowania).
|
||||
[[nodiscard]] Eu7PackBench const &
|
||||
pack_bench_bootstrap();
|
||||
|
||||
[[nodiscard]] bool
|
||||
pack_bench_stream_phase_active();
|
||||
|
||||
void
|
||||
reset_pack_bench();
|
||||
|
||||
// Wywolac raz przy dismiss overlay — oddziela bootstrap od streamu.
|
||||
void
|
||||
pack_bench_begin_stream_phase();
|
||||
|
||||
void
|
||||
pack_bench_note_ready_queue( std::size_t Size );
|
||||
|
||||
void
|
||||
pack_bench_note_in_flight( std::size_t Size );
|
||||
|
||||
void
|
||||
pack_bench_note_pending_cold_meshes( std::size_t Size );
|
||||
|
||||
void
|
||||
pack_bench_inc( std::uint64_t Eu7PackBench::*Field, std::uint64_t Amount = 1 );
|
||||
|
||||
void
|
||||
pack_bench_note_chunk(
|
||||
double WallMs,
|
||||
std::size_t Instances,
|
||||
std::size_t ColdMeshes,
|
||||
double ColdMs,
|
||||
double WarmTexMs,
|
||||
double PointerApplyMs );
|
||||
|
||||
void
|
||||
log_pack_texture_fail( std::string const &TexturePath );
|
||||
|
||||
void
|
||||
log_pack_bench();
|
||||
|
||||
void
|
||||
log_pack_stream_bench();
|
||||
|
||||
// Na wyjsciu z jazdy / reset sceny — jednorazowy dump bez czekania na timer.
|
||||
void
|
||||
flush_pack_stream_bench();
|
||||
|
||||
// Co kilka sekund / przy szybkiej kamerze — stan kolejek i ringu.
|
||||
void
|
||||
log_pack_stream_status(
|
||||
glm::dvec3 const &CameraPosition,
|
||||
double CameraSpeedMps,
|
||||
float InnerRingProgress,
|
||||
float OuterRingProgress,
|
||||
std::size_t ReadyQueue,
|
||||
std::size_t InFlight,
|
||||
std::size_t PendingOffset,
|
||||
std::size_t PendingTotal,
|
||||
double DrainBudgetMs,
|
||||
std::size_t ChunkLimit );
|
||||
|
||||
} // namespace scene::eu7
|
||||
282
scene/eu7/eu7_parameters.cpp
Normal file
282
scene/eu7/eu7_parameters.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "scene/eu7/eu7_parameters.h"
|
||||
#include "scene/eu7/eu7_apply.h"
|
||||
#include "scene/eu7/eu7_transform.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace scene::eu7 {
|
||||
namespace {
|
||||
|
||||
void
|
||||
bind_include_parameter_impl( std::string &text, std::vector<std::string> const ¶meters, bool const to_lower ) {
|
||||
if( parameters.empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t pos { 0 };
|
||||
while( ( pos = text.find( "(p", pos ) ) != std::string::npos ) {
|
||||
auto const close { text.find( ')', pos ) };
|
||||
if( close == std::string::npos ) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto const index_text { text.substr( pos + 2, close - ( pos + 2 ) ) };
|
||||
text.erase( pos, ( close - pos ) + 1 );
|
||||
|
||||
auto const index { static_cast<std::size_t>( std::atoi( index_text.c_str() ) ) };
|
||||
auto const replacement { (
|
||||
index >= 1 && ( index - 1 ) < parameters.size() ) ?
|
||||
parameters[ index - 1 ] :
|
||||
std::string{ "none" } };
|
||||
|
||||
text.insert( pos, replacement );
|
||||
|
||||
if( to_lower ) {
|
||||
for( std::size_t i { pos }; i < pos + replacement.size(); ++i ) {
|
||||
text[ i ] = static_cast<char>( tolower( static_cast<unsigned char>( text[ i ] ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
pos += replacement.size();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
bind_name( std::string &text, std::vector<std::string> const ¶meters ) {
|
||||
bind_include_parameter_impl( text, parameters, true );
|
||||
}
|
||||
|
||||
void
|
||||
bind_path( std::string &text, std::vector<std::string> const ¶meters ) {
|
||||
bind_include_parameter_impl( text, parameters, false );
|
||||
}
|
||||
|
||||
void
|
||||
bind_optional_name( std::optional<std::string> &text, std::vector<std::string> const ¶meters ) {
|
||||
if( text ) {
|
||||
bind_name( *text, parameters );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
bind_node_strings( Eu7BasicNode &node, std::vector<std::string> const ¶meters ) {
|
||||
bind_name( node.name, parameters );
|
||||
bind_name( node.node_type, parameters );
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
bind_include_parameter( std::string &text, std::vector<std::string> const ¶meters, bool const to_lower ) {
|
||||
bind_include_parameter_impl( text, parameters, to_lower );
|
||||
}
|
||||
|
||||
void
|
||||
apply_include_parameters_to_models( std::vector<Eu7Model> &models, std::vector<std::string> const ¶meters ) {
|
||||
if( parameters.empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for( auto &model : models ) {
|
||||
bind_node_strings( model.node, parameters );
|
||||
bind_path( model.model_file, parameters );
|
||||
bind_path( model.texture_file, parameters );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
apply_include_parameters_to_scene( Eu7Scene &scene, std::vector<std::string> const ¶meters ) {
|
||||
if( parameters.empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for( auto &track : scene.tracks ) {
|
||||
bind_node_strings( track.node, parameters );
|
||||
if( track.visibility ) {
|
||||
bind_path( track.visibility->material1, parameters );
|
||||
bind_path( track.visibility->material2, parameters );
|
||||
}
|
||||
for( auto &[key, value] : track.tail_keywords ) {
|
||||
bind_name( key, parameters );
|
||||
bind_name( value, parameters );
|
||||
}
|
||||
}
|
||||
|
||||
for( auto &traction : scene.traction ) {
|
||||
bind_node_strings( traction.node, parameters );
|
||||
bind_name( traction.power_supply_name, parameters );
|
||||
bind_name( traction.material_raw, parameters );
|
||||
bind_optional_name( traction.parallel_name, parameters );
|
||||
}
|
||||
|
||||
for( auto &source : scene.power_sources ) {
|
||||
bind_node_strings( source.node, parameters );
|
||||
}
|
||||
|
||||
for( auto &shape : scene.shapes ) {
|
||||
bind_node_strings( shape.node, parameters );
|
||||
bind_path( shape.material_path, parameters );
|
||||
}
|
||||
|
||||
for( auto &shape : scene.terrain_shapes ) {
|
||||
bind_node_strings( shape.node, parameters );
|
||||
bind_path( shape.material_path, parameters );
|
||||
}
|
||||
|
||||
for( auto &lines : scene.lines ) {
|
||||
bind_node_strings( lines.node, parameters );
|
||||
}
|
||||
|
||||
apply_include_parameters_to_models( scene.models, parameters );
|
||||
|
||||
for( auto &cell : scene.memcells ) {
|
||||
bind_node_strings( cell.node, parameters );
|
||||
bind_name( cell.text, parameters );
|
||||
bind_optional_name( cell.track_name, parameters );
|
||||
}
|
||||
|
||||
for( auto &launcher : scene.event_launchers ) {
|
||||
bind_node_strings( launcher.node, parameters );
|
||||
bind_name( launcher.activation_key_raw, parameters );
|
||||
bind_name( launcher.event1_name, parameters );
|
||||
bind_name( launcher.event2_name, parameters );
|
||||
if( launcher.condition ) {
|
||||
bind_name( launcher.condition->memcell_name, parameters );
|
||||
bind_name( launcher.condition->compare_text, parameters );
|
||||
}
|
||||
}
|
||||
|
||||
for( auto &vehicle : scene.dynamics ) {
|
||||
bind_node_strings( vehicle.node, parameters );
|
||||
bind_path( vehicle.data_folder, parameters );
|
||||
bind_path( vehicle.skin_file, parameters );
|
||||
bind_path( vehicle.mmd_file, parameters );
|
||||
bind_name( vehicle.track_name, parameters );
|
||||
bind_name( vehicle.driver_type, parameters );
|
||||
bind_name( vehicle.coupling_raw, parameters );
|
||||
bind_path( vehicle.coupling_params, parameters );
|
||||
bind_name( vehicle.load_type, parameters );
|
||||
bind_optional_name( vehicle.destination, parameters );
|
||||
}
|
||||
|
||||
for( auto &sound : scene.sounds ) {
|
||||
bind_node_strings( sound.node, parameters );
|
||||
bind_path( sound.wav_file, parameters );
|
||||
}
|
||||
|
||||
for( auto &event : scene.events ) {
|
||||
bind_name( event.name, parameters );
|
||||
for( auto &target : event.targets ) {
|
||||
bind_name( target, parameters );
|
||||
}
|
||||
for( auto &[key, value] : event.payload ) {
|
||||
bind_name( key, parameters );
|
||||
bind_name( value, parameters );
|
||||
}
|
||||
}
|
||||
|
||||
for( auto &trainset : scene.trainsets ) {
|
||||
bind_name( trainset.name, parameters );
|
||||
bind_name( trainset.track, parameters );
|
||||
std::unordered_map<std::string, std::string> rebound;
|
||||
rebound.reserve( trainset.assignment.size() );
|
||||
for( auto const &[key, value] : trainset.assignment ) {
|
||||
auto bound_key { key };
|
||||
auto bound_value { value };
|
||||
bind_name( bound_key, parameters );
|
||||
bind_name( bound_value, parameters );
|
||||
rebound.emplace( std::move( bound_key ), std::move( bound_value ) );
|
||||
}
|
||||
trainset.assignment = std::move( rebound );
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] double
|
||||
resolve_placement_param(
|
||||
std::uint8_t const param_index,
|
||||
std::vector<std::string> const ¶meters ) {
|
||||
if( param_index == 0 ) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
auto const index { static_cast<std::size_t>( param_index ) };
|
||||
if( index < 1 || index > parameters.size() ) {
|
||||
throw std::runtime_error( "EU7: brak parametru p" + std::to_string( param_index ) );
|
||||
}
|
||||
|
||||
return std::stod( parameters[ index - 1 ] );
|
||||
}
|
||||
|
||||
Eu7TransformContext
|
||||
placement_transform_from_include_parameters(
|
||||
Eu7IncludePlacement const &binding,
|
||||
std::vector<std::string> const ¶meters ) {
|
||||
Eu7TransformContext placement;
|
||||
if( binding.empty() ) {
|
||||
return placement;
|
||||
}
|
||||
|
||||
try {
|
||||
auto const origin_x { resolve_placement_param( binding.origin_x_param, parameters ) };
|
||||
auto const origin_y { resolve_placement_param( binding.origin_y_param, parameters ) };
|
||||
auto const origin_z { resolve_placement_param( binding.origin_z_param, parameters ) };
|
||||
placement.origin_stack.push_back( { origin_x, origin_y, origin_z } );
|
||||
placement.rotation.y = resolve_placement_param( binding.rotation_y_param, parameters );
|
||||
}
|
||||
catch( std::exception const & ) {
|
||||
placement = {};
|
||||
}
|
||||
|
||||
return placement;
|
||||
}
|
||||
|
||||
void
|
||||
apply_include_placement_to_scene(
|
||||
Eu7Scene &scene,
|
||||
Eu7IncludePlacement const &binding,
|
||||
std::vector<std::string> const ¶meters ) {
|
||||
auto const placement {
|
||||
placement_transform_from_include_parameters( binding, parameters ) };
|
||||
if( false == transform_is_empty( placement ) ) {
|
||||
compose_scene_with_include_prefix( scene, placement );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
apply_include_placement_to_models(
|
||||
std::vector<Eu7Model> &models,
|
||||
Eu7IncludePlacement const &binding,
|
||||
std::vector<std::string> const ¶meters ) {
|
||||
auto const placement {
|
||||
placement_transform_from_include_parameters( binding, parameters ) };
|
||||
if( false == transform_is_empty( placement ) ) {
|
||||
compose_models_with_prefix( models, placement );
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
module_load_key(
|
||||
std::string const &resolved_path,
|
||||
std::vector<std::string> const ¶meters ) {
|
||||
std::string key { resolved_path };
|
||||
for( auto const ¶meter : parameters ) {
|
||||
key.push_back( '\0' );
|
||||
key += parameter;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
} // namespace scene::eu7
|
||||
51
scene/eu7/eu7_parameters.h
Normal file
51
scene/eu7/eu7_parameters.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/eu7/eu7_types.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
void
|
||||
bind_include_parameter( std::string &text, std::vector<std::string> const ¶meters, bool to_lower = false );
|
||||
|
||||
void
|
||||
apply_include_parameters_to_scene( Eu7Scene &scene, std::vector<std::string> const ¶meters );
|
||||
|
||||
void
|
||||
apply_include_parameters_to_models( std::vector<Eu7Model> &models, std::vector<std::string> const ¶meters );
|
||||
|
||||
// Placement z chunku PLAC (indeksy pN z origin/rotate w .inc) + wartosci z INCL.
|
||||
[[nodiscard]] Eu7TransformContext
|
||||
placement_transform_from_include_parameters(
|
||||
Eu7IncludePlacement const &binding,
|
||||
std::vector<std::string> const ¶meters );
|
||||
|
||||
void
|
||||
apply_include_placement_to_scene(
|
||||
Eu7Scene &scene,
|
||||
Eu7IncludePlacement const &binding,
|
||||
std::vector<std::string> const ¶meters );
|
||||
|
||||
void
|
||||
apply_include_placement_to_models(
|
||||
std::vector<Eu7Model> &models,
|
||||
Eu7IncludePlacement const &binding,
|
||||
std::vector<std::string> const ¶meters );
|
||||
|
||||
[[nodiscard]] std::string
|
||||
module_load_key(
|
||||
std::string const &resolved_path,
|
||||
std::vector<std::string> const ¶meters );
|
||||
|
||||
} // namespace scene::eu7
|
||||
1149
scene/eu7/eu7_reader.cpp
Normal file
1149
scene/eu7/eu7_reader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
61
scene/eu7/eu7_reader.h
Normal file
61
scene/eu7/eu7_reader.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/eu7/eu7_types.h"
|
||||
|
||||
#include <istream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
[[nodiscard]] Eu7Module
|
||||
read_module( std::string const &Path );
|
||||
|
||||
[[nodiscard]] bool
|
||||
is_valid_eu7b_file( std::string const &Path );
|
||||
|
||||
// Szuka wpisu PIDX dla sekcji 1 km (row/column jak basic_region::section).
|
||||
[[nodiscard]] std::optional<Eu7PackIndexEntry>
|
||||
find_pack_entry( Eu7Module const &Module, int Row, int Column );
|
||||
|
||||
// Odczyt MODL z chunku PACK dla jednej sekcji (world-space, po bake).
|
||||
[[nodiscard]] std::vector<Eu7Model>
|
||||
read_pack_section( Eu7Module const &Module, int Row, int Column );
|
||||
|
||||
// Seek do pack_offset z PIDX i parsuj naglowek sekcji (v7/v8).
|
||||
void
|
||||
seek_pack_section(
|
||||
Eu7Module const &Module,
|
||||
std::istream &Input,
|
||||
Eu7PackIndexEntry const &Entry,
|
||||
Eu7PackSectionCursor &Cursor );
|
||||
|
||||
// Odczyt do max_count modeli z biezacej pozycji strumienia (po seek_pack_section).
|
||||
[[nodiscard]] std::vector<Eu7Model>
|
||||
read_pack_models_chunk(
|
||||
Eu7Module const &Module,
|
||||
std::istream &Input,
|
||||
Eu7PackSectionCursor &Cursor,
|
||||
std::size_t MaxCount );
|
||||
|
||||
// Wznowienie odczytu sub-chunka (offset wzgledem pack_offset sekcji z PIDX).
|
||||
void
|
||||
resume_pack_section(
|
||||
Eu7Module const &Module,
|
||||
std::istream &Input,
|
||||
Eu7PackIndexEntry const &Entry,
|
||||
std::uint64_t ResumeByteOffset,
|
||||
Eu7PackSectionCursor ResumeCursor,
|
||||
Eu7PackSectionCursor &Cursor );
|
||||
|
||||
} // namespace scene::eu7
|
||||
47
scene/eu7/eu7_section.cpp
Normal file
47
scene/eu7/eu7_section.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "scene/eu7/eu7_section.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
std::pair<int, int>
|
||||
section_row_column( glm::dvec3 const &world_location ) {
|
||||
auto const column {
|
||||
static_cast<int>( std::floor(
|
||||
world_location.x / static_cast<double>( kSectionSizeM ) +
|
||||
kRegionSideSectionCount / 2 ) ) };
|
||||
auto const row {
|
||||
static_cast<int>( std::floor(
|
||||
world_location.z / static_cast<double>( kSectionSizeM ) +
|
||||
kRegionSideSectionCount / 2 ) ) };
|
||||
|
||||
return {
|
||||
std::clamp( row, 0, kRegionSideSectionCount - 1 ),
|
||||
std::clamp( column, 0, kRegionSideSectionCount - 1 ) };
|
||||
}
|
||||
|
||||
std::size_t
|
||||
section_index( glm::dvec3 const &world_location ) {
|
||||
auto const [row, column] { section_row_column( world_location ) };
|
||||
return section_index( row, column );
|
||||
}
|
||||
|
||||
std::size_t
|
||||
section_index( int const row, int const column ) {
|
||||
return (
|
||||
static_cast<std::size_t>( row ) * static_cast<std::size_t>( kRegionSideSectionCount ) +
|
||||
static_cast<std::size_t>( column ) );
|
||||
}
|
||||
|
||||
} // namespace scene::eu7
|
||||
33
scene/eu7/eu7_section.h
Normal file
33
scene/eu7/eu7_section.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
// Musi byc zgodne z scene::EU07_SECTIONSIZE / EU07_REGIONSIDESECTIONCOUNT.
|
||||
constexpr int kSectionSizeM { 1000 };
|
||||
constexpr int kRegionSideSectionCount { 500 };
|
||||
|
||||
[[nodiscard]] std::pair<int, int>
|
||||
section_row_column( glm::dvec3 const &world_location );
|
||||
|
||||
[[nodiscard]] std::size_t
|
||||
section_index( glm::dvec3 const &world_location );
|
||||
|
||||
[[nodiscard]] std::size_t
|
||||
section_index( int const row, int const column );
|
||||
|
||||
} // namespace scene::eu7
|
||||
2042
scene/eu7/eu7_section_stream.cpp
Normal file
2042
scene/eu7/eu7_section_stream.cpp
Normal file
File diff suppressed because it is too large
Load Diff
105
scene/eu7/eu7_section_stream.h
Normal file
105
scene/eu7/eu7_section_stream.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/eu7/eu7_types.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace simulation {
|
||||
class state_serializer;
|
||||
}
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
constexpr int kSectionStreamBootstrapRadiusKm { 3 };
|
||||
constexpr int kSectionStreamGameplayRadiusKm { 4 };
|
||||
|
||||
void
|
||||
init_section_stream(
|
||||
Eu7Module const &RootModule,
|
||||
std::string const &ResolvedPath,
|
||||
simulation::state_serializer &Serializer );
|
||||
|
||||
void
|
||||
prime_section_stream( Eu7Module const &RootModule );
|
||||
|
||||
[[nodiscard]] glm::dvec3
|
||||
resolve_stream_position();
|
||||
|
||||
// Opcjonalnie blokuje do zaladowania pierścienia bootstrap wokol pozycji (debug/narzedzia).
|
||||
void
|
||||
bootstrap_section_stream( glm::dvec3 const &WorldPosition );
|
||||
|
||||
void
|
||||
update_section_stream( glm::dvec3 const &WorldPosition );
|
||||
|
||||
// Worker dekoduje sekcje; watek mapy wstawia po wskazniku do bufora (bez re-read pliku).
|
||||
void
|
||||
drain_section_stream(
|
||||
std::size_t MaxColdMeshes = 0,
|
||||
std::size_t MaxInstances = 0 );
|
||||
|
||||
[[nodiscard]] bool
|
||||
section_stream_active();
|
||||
|
||||
[[nodiscard]] bool
|
||||
section_stream_needs_bootstrap();
|
||||
|
||||
// Po wejsciu w symulacje: kolejkuje bootstrap bez blokowania petli renderu.
|
||||
void
|
||||
kick_section_stream_bootstrap();
|
||||
|
||||
// Blokujacy bootstrap (preload / narzedzia).
|
||||
void
|
||||
try_bootstrap_section_stream();
|
||||
|
||||
// Po zaladowaniu scenariusza: bootstrap + drain PACK przed wejsciem w jazde.
|
||||
void
|
||||
preload_section_stream( double MaxDrainMs = 300.0 );
|
||||
|
||||
// Wszystkie sekcje PACK w pierścieniu RadiusKm wokol pozycji wczytane i zaaplikowane.
|
||||
[[nodiscard]] bool
|
||||
section_stream_ready_around(
|
||||
glm::dvec3 const &WorldPosition,
|
||||
int RadiusKm = kSectionStreamBootstrapRadiusKm );
|
||||
|
||||
// ready_around stabilnie przez kilka klatek — bez migania overlay/sceny.
|
||||
[[nodiscard]] bool
|
||||
section_stream_presentable_around(
|
||||
glm::dvec3 const &WorldPosition,
|
||||
int RadiusKm = kSectionStreamBootstrapRadiusKm );
|
||||
|
||||
// Renderer/UI: trzymaj czarny ekran + overlay, nie rysuj swiata 3D.
|
||||
// Jednorazowo przy starcie — po dismiss nie wraca przy streamingu w locie.
|
||||
[[nodiscard]] bool
|
||||
loading_screen_blocks_world(
|
||||
glm::dvec3 const &WorldPosition,
|
||||
int RadiusKm = kSectionStreamBootstrapRadiusKm );
|
||||
|
||||
[[nodiscard]] bool
|
||||
loading_screen_dismissed();
|
||||
|
||||
void
|
||||
dismiss_loading_screen();
|
||||
|
||||
[[nodiscard]] glm::dvec3
|
||||
stream_loading_position();
|
||||
|
||||
// 0..1 — ile sekcji PACK z pierścienia juz siedzi w Region.
|
||||
[[nodiscard]] float
|
||||
section_stream_ring_progress(
|
||||
glm::dvec3 const &WorldPosition,
|
||||
int RadiusKm = kSectionStreamBootstrapRadiusKm );
|
||||
|
||||
void
|
||||
reset_section_stream();
|
||||
|
||||
} // namespace scene::eu7
|
||||
258
scene/eu7/eu7_transform.h
Normal file
258
scene/eu7/eu7_transform.h
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/eu7/eu7_types.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline constexpr double kPi { 3.14159265358979323846 };
|
||||
|
||||
struct ScratchLocation {
|
||||
std::vector<glm::dvec3> offset_stack;
|
||||
std::vector<glm::dvec3> scale_stack;
|
||||
glm::dvec3 rotation{};
|
||||
};
|
||||
|
||||
[[nodiscard]] inline ScratchLocation
|
||||
scratch_from_transform( Eu7TransformContext const &transform ) {
|
||||
ScratchLocation scratch;
|
||||
scratch.offset_stack = transform.origin_stack;
|
||||
scratch.scale_stack = transform.scale_stack;
|
||||
scratch.rotation = transform.rotation;
|
||||
return scratch;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
[[nodiscard]] inline glm::dvec3
|
||||
inverse_transform_point( glm::dvec3 location, Eu7TransformContext const &transform ) {
|
||||
auto const scratch { detail::scratch_from_transform( transform ) };
|
||||
|
||||
if( !scratch.offset_stack.empty() ) {
|
||||
auto const &off { scratch.offset_stack.back() };
|
||||
location.x -= off.x;
|
||||
location.y -= off.y;
|
||||
location.z -= off.z;
|
||||
}
|
||||
|
||||
if( !scratch.scale_stack.empty() ) {
|
||||
auto const &sc { scratch.scale_stack.back() };
|
||||
if( sc.x != 0.0 ) {
|
||||
location.x /= sc.x;
|
||||
}
|
||||
if( sc.y != 0.0 ) {
|
||||
location.y /= sc.y;
|
||||
}
|
||||
if( sc.z != 0.0 ) {
|
||||
location.z /= sc.z;
|
||||
}
|
||||
}
|
||||
|
||||
if( scratch.rotation.y != 0.0 ) {
|
||||
auto const rad { -scratch.rotation.y * ( detail::kPi / 180.0 ) };
|
||||
auto const c { std::cos( rad ) };
|
||||
auto const s { std::sin( rad ) };
|
||||
auto const x { location.x * c + location.z * s };
|
||||
auto const z { -location.x * s + location.z * c };
|
||||
location.x = x;
|
||||
location.z = z;
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
inline void
|
||||
inverse_transform_shape_vertices(
|
||||
std::vector<Eu7WorldVertex> &vertices,
|
||||
Eu7TransformContext const &transform ) {
|
||||
auto const scratch { detail::scratch_from_transform( transform ) };
|
||||
|
||||
if( !scratch.offset_stack.empty() ) {
|
||||
auto const &off { scratch.offset_stack.back() };
|
||||
for( auto &vtx : vertices ) {
|
||||
vtx.position.x -= off.x;
|
||||
vtx.position.y -= off.y;
|
||||
vtx.position.z -= off.z;
|
||||
}
|
||||
}
|
||||
|
||||
if( scratch.rotation.x != 0.0 || scratch.rotation.y != 0.0 || scratch.rotation.z != 0.0 ) {
|
||||
auto const rx { -scratch.rotation.x * ( detail::kPi / 180.0 ) };
|
||||
auto const ry { -scratch.rotation.y * ( detail::kPi / 180.0 ) };
|
||||
auto const rz { -scratch.rotation.z * ( detail::kPi / 180.0 ) };
|
||||
|
||||
auto rotate_y = [&]( glm::dvec3 &v ) {
|
||||
auto const c { std::cos( ry ) };
|
||||
auto const s { std::sin( ry ) };
|
||||
auto const x { v.x * c + v.z * s };
|
||||
auto const z { -v.x * s + v.z * c };
|
||||
v.x = x;
|
||||
v.z = z;
|
||||
};
|
||||
auto rotate_x = [&]( glm::dvec3 &v ) {
|
||||
auto const c { std::cos( rx ) };
|
||||
auto const s { std::sin( rx ) };
|
||||
auto const y { v.y * c - v.z * s };
|
||||
auto const z { v.y * s + v.z * c };
|
||||
v.y = y;
|
||||
v.z = z;
|
||||
};
|
||||
auto rotate_z = [&]( glm::dvec3 &v ) {
|
||||
auto const c { std::cos( rz ) };
|
||||
auto const s { std::sin( rz ) };
|
||||
auto const x { v.x * c - v.y * s };
|
||||
auto const y { v.x * s + v.y * c };
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
};
|
||||
|
||||
for( auto &vtx : vertices ) {
|
||||
rotate_y( vtx.position );
|
||||
rotate_x( vtx.position );
|
||||
rotate_z( vtx.position );
|
||||
glm::dvec3 n { vtx.normal.x, vtx.normal.y, vtx.normal.z };
|
||||
rotate_y( n );
|
||||
rotate_x( n );
|
||||
rotate_z( n );
|
||||
vtx.normal = glm::vec3( static_cast<float>( n.x ), static_cast<float>( n.y ), static_cast<float>( n.z ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
transform_is_empty( Eu7TransformContext const &transform ) {
|
||||
return (
|
||||
transform.origin_stack.empty() &&
|
||||
transform.scale_stack.empty() &&
|
||||
transform.rotation.x == 0.0 &&
|
||||
transform.rotation.y == 0.0 &&
|
||||
transform.rotation.z == 0.0 &&
|
||||
transform.group_depth == 0 );
|
||||
}
|
||||
|
||||
[[nodiscard]] inline glm::dvec3
|
||||
transform_point( glm::dvec3 location, Eu7TransformContext const &transform ) {
|
||||
if( transform.rotation.y != 0.0 ) {
|
||||
auto const rad { transform.rotation.y * ( detail::kPi / 180.0 ) };
|
||||
auto const c { std::cos( rad ) };
|
||||
auto const s { std::sin( rad ) };
|
||||
auto const x { location.x * c + location.z * s };
|
||||
auto const z { -location.x * s + location.z * c };
|
||||
location.x = x;
|
||||
location.z = z;
|
||||
}
|
||||
|
||||
if( !transform.scale_stack.empty() ) {
|
||||
auto const &sc { transform.scale_stack.back() };
|
||||
if( sc.x != 0.0 ) {
|
||||
location.x *= sc.x;
|
||||
}
|
||||
if( sc.y != 0.0 ) {
|
||||
location.y *= sc.y;
|
||||
}
|
||||
if( sc.z != 0.0 ) {
|
||||
location.z *= sc.z;
|
||||
}
|
||||
}
|
||||
|
||||
if( !transform.origin_stack.empty() ) {
|
||||
auto const &off { transform.origin_stack.back() };
|
||||
location.x += off.x;
|
||||
location.y += off.y;
|
||||
location.z += off.z;
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
// Wzgledne wektory kontrolne toru (cp_out/cp_in) — rotacja i skala, bez translacji origin.
|
||||
[[nodiscard]] inline glm::dvec3
|
||||
transform_vector( glm::dvec3 vector, Eu7TransformContext const &transform ) {
|
||||
if( transform.rotation.y != 0.0 ) {
|
||||
auto const rad { transform.rotation.y * ( detail::kPi / 180.0 ) };
|
||||
auto const c { std::cos( rad ) };
|
||||
auto const s { std::sin( rad ) };
|
||||
auto const x { vector.x * c + vector.z * s };
|
||||
auto const z { -vector.x * s + vector.z * c };
|
||||
vector.x = x;
|
||||
vector.z = z;
|
||||
}
|
||||
|
||||
if( !transform.scale_stack.empty() ) {
|
||||
auto const &sc { transform.scale_stack.back() };
|
||||
if( sc.x != 0.0 ) {
|
||||
vector.x *= sc.x;
|
||||
}
|
||||
if( sc.y != 0.0 ) {
|
||||
vector.y *= sc.y;
|
||||
}
|
||||
if( sc.z != 0.0 ) {
|
||||
vector.z *= sc.z;
|
||||
}
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
inline void
|
||||
compose_node_transform( Eu7TransformContext &node, Eu7TransformContext const &prefix ) {
|
||||
if( transform_is_empty( prefix ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const parent_origin {
|
||||
prefix.origin_stack.empty() ?
|
||||
glm::dvec3{} :
|
||||
prefix.origin_stack.back() };
|
||||
for( auto &offset : node.origin_stack ) {
|
||||
offset.x += parent_origin.x;
|
||||
offset.y += parent_origin.y;
|
||||
offset.z += parent_origin.z;
|
||||
}
|
||||
|
||||
auto const parent_scale {
|
||||
prefix.scale_stack.empty() ?
|
||||
glm::dvec3{ 1.0, 1.0, 1.0 } :
|
||||
prefix.scale_stack.back() };
|
||||
for( auto &scale : node.scale_stack ) {
|
||||
scale.x *= parent_scale.x;
|
||||
scale.y *= parent_scale.y;
|
||||
scale.z *= parent_scale.z;
|
||||
}
|
||||
|
||||
node.rotation.x += prefix.rotation.x;
|
||||
node.rotation.y += prefix.rotation.y;
|
||||
node.rotation.z += prefix.rotation.z;
|
||||
|
||||
node.group_depth += prefix.group_depth;
|
||||
}
|
||||
|
||||
void
|
||||
compose_scene_with_include_prefix( Eu7Scene &scene, Eu7TransformContext const &prefix );
|
||||
|
||||
[[nodiscard]] inline glm::dvec3
|
||||
subtract_origin_offset( glm::dvec3 const &world, Eu7TransformContext const &transform ) {
|
||||
glm::dvec3 local { world };
|
||||
if( !transform.origin_stack.empty() ) {
|
||||
auto const &off { transform.origin_stack.back() };
|
||||
local.x -= off.x;
|
||||
local.y -= off.y;
|
||||
local.z -= off.z;
|
||||
}
|
||||
return local;
|
||||
}
|
||||
|
||||
} // namespace scene::eu7
|
||||
393
scene/eu7/eu7_types.h
Normal file
393
scene/eu7/eu7_types.h
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace scene::eu7 {
|
||||
|
||||
struct Eu7Vec4 {
|
||||
float x = 0.f;
|
||||
float y = 0.f;
|
||||
float z = 0.f;
|
||||
float w = 1.f;
|
||||
};
|
||||
|
||||
struct Eu7WorldVertex {
|
||||
glm::dvec3 position{};
|
||||
glm::vec3 normal{};
|
||||
double u = 0.0;
|
||||
double v = 0.0;
|
||||
};
|
||||
|
||||
struct Eu7LightingData {
|
||||
Eu7Vec4 diffuse{ 0.8f, 0.8f, 0.8f, 1.f };
|
||||
Eu7Vec4 ambient{ 0.2f, 0.2f, 0.2f, 1.f };
|
||||
Eu7Vec4 specular{ 0.f, 0.f, 0.f, 1.f };
|
||||
};
|
||||
|
||||
struct Eu7BoundingArea {
|
||||
glm::dvec3 center{};
|
||||
float radius = -1.f;
|
||||
};
|
||||
|
||||
struct Eu7TransformContext {
|
||||
std::vector<glm::dvec3> origin_stack;
|
||||
std::vector<glm::dvec3> scale_stack;
|
||||
glm::dvec3 rotation{};
|
||||
std::size_t group_depth = 0;
|
||||
};
|
||||
|
||||
struct Eu7BasicNode {
|
||||
Eu7BoundingArea area;
|
||||
double range_squared_min = 0.0;
|
||||
double range_squared_max = std::numeric_limits<double>::max();
|
||||
bool visible = true;
|
||||
std::string name;
|
||||
std::string node_type;
|
||||
std::size_t group_handle = 0;
|
||||
bool group_valid = false;
|
||||
Eu7TransformContext transform;
|
||||
};
|
||||
|
||||
struct Eu7SegmentPath {
|
||||
glm::dvec3 p_start{};
|
||||
double roll_start = 0.0;
|
||||
glm::dvec3 cp_out{};
|
||||
glm::dvec3 cp_in{};
|
||||
glm::dvec3 p_end{};
|
||||
double roll_end = 0.0;
|
||||
double radius = 0.0;
|
||||
};
|
||||
|
||||
enum class Eu7TrackType : std::uint8_t {
|
||||
Unknown,
|
||||
Normal,
|
||||
Switch,
|
||||
Table,
|
||||
Cross,
|
||||
Tributary,
|
||||
};
|
||||
|
||||
enum class Eu7TrackCategory : std::uint8_t {
|
||||
Rail = 1,
|
||||
Road = 2,
|
||||
Water = 4,
|
||||
};
|
||||
|
||||
enum class Eu7TrackEnvironment : std::int8_t {
|
||||
Unknown = -1,
|
||||
Flat = 0,
|
||||
Mountains,
|
||||
Canyon,
|
||||
Tunnel,
|
||||
Bridge,
|
||||
Bank,
|
||||
};
|
||||
|
||||
struct Eu7TrackVisibility {
|
||||
std::string material1;
|
||||
float tex_length = 4.f;
|
||||
std::string material2;
|
||||
float tex_height1 = 0.f;
|
||||
float tex_width = 0.f;
|
||||
float tex_slope = 0.f;
|
||||
};
|
||||
|
||||
struct Eu7Track {
|
||||
Eu7BasicNode node;
|
||||
Eu7TrackType track_type = Eu7TrackType::Unknown;
|
||||
Eu7TrackCategory category = Eu7TrackCategory::Rail;
|
||||
float length = 0.f;
|
||||
float track_width = 0.f;
|
||||
float friction = 0.f;
|
||||
float sound_distance = 0.f;
|
||||
int quality_flag = 0;
|
||||
int damage_flag = 0;
|
||||
Eu7TrackEnvironment environment = Eu7TrackEnvironment::Unknown;
|
||||
std::optional<Eu7TrackVisibility> visibility;
|
||||
std::vector<Eu7SegmentPath> paths;
|
||||
std::vector<std::pair<std::string, std::string>> tail_keywords;
|
||||
};
|
||||
|
||||
enum class Eu7TractionWireMaterial : std::uint8_t {
|
||||
None = 0,
|
||||
Copper = 1,
|
||||
Aluminium = 2,
|
||||
};
|
||||
|
||||
struct Eu7Traction {
|
||||
Eu7BasicNode node;
|
||||
std::string power_supply_name;
|
||||
float nominal_voltage = 0.f;
|
||||
float max_current = 0.f;
|
||||
float resistivity_ohm_per_m = 0.f;
|
||||
double resistivity_legacy = 0.0;
|
||||
std::string material_raw = "cu";
|
||||
Eu7TractionWireMaterial material = Eu7TractionWireMaterial::Copper;
|
||||
float wire_thickness = 0.f;
|
||||
int damage_flag = 0;
|
||||
glm::dvec3 wire_p1{};
|
||||
glm::dvec3 wire_p2{};
|
||||
glm::dvec3 wire_p3{};
|
||||
glm::dvec3 wire_p4{};
|
||||
double min_height = 0.0;
|
||||
double segment_length = 0.0;
|
||||
int wire_count = 0;
|
||||
float wire_offset = 0.f;
|
||||
std::optional<std::string> parallel_name;
|
||||
};
|
||||
|
||||
enum class Eu7PowerSourceModifier : std::uint8_t {
|
||||
None,
|
||||
Recuperation,
|
||||
Section,
|
||||
};
|
||||
|
||||
struct Eu7TractionPowerSource {
|
||||
Eu7BasicNode node;
|
||||
glm::dvec3 position{};
|
||||
float nominal_voltage = 0.f;
|
||||
float voltage_frequency = 0.f;
|
||||
double internal_resistance_legacy = 0.2;
|
||||
float internal_resistance = 0.2f;
|
||||
float max_output_current = 0.f;
|
||||
float fast_fuse_timeout = 0.f;
|
||||
float fast_fuse_repetition = 0.f;
|
||||
float slow_fuse_timeout = 0.f;
|
||||
Eu7PowerSourceModifier modifier = Eu7PowerSourceModifier::None;
|
||||
};
|
||||
|
||||
struct Eu7Shape {
|
||||
Eu7BasicNode node;
|
||||
bool translucent = false;
|
||||
std::string material_path;
|
||||
Eu7LightingData lighting;
|
||||
glm::dvec3 origin{};
|
||||
std::vector<Eu7WorldVertex> vertices;
|
||||
};
|
||||
|
||||
struct Eu7Lines {
|
||||
Eu7BasicNode node;
|
||||
Eu7LightingData lighting;
|
||||
float line_width = 1.f;
|
||||
glm::dvec3 origin{};
|
||||
std::vector<Eu7WorldVertex> vertices;
|
||||
};
|
||||
|
||||
struct Eu7Model {
|
||||
Eu7BasicNode node;
|
||||
glm::dvec3 location{};
|
||||
glm::dvec3 angles{};
|
||||
glm::dvec3 scale{ 1.0, 1.0, 1.0 };
|
||||
std::string model_file;
|
||||
std::string texture_file;
|
||||
std::vector<float> light_states;
|
||||
std::vector<std::uint32_t> light_colors;
|
||||
bool transition = true;
|
||||
bool is_terrain = false;
|
||||
};
|
||||
|
||||
// Wspolna definicja modelu (EU7B v8 PROT) — bez transformacji instancji.
|
||||
struct Eu7ModelPrototype {
|
||||
Eu7BasicNode node;
|
||||
std::string model_file;
|
||||
std::string texture_file;
|
||||
std::vector<float> light_states;
|
||||
std::vector<std::uint32_t> light_colors;
|
||||
bool transition = true;
|
||||
bool is_terrain = false;
|
||||
};
|
||||
|
||||
struct Eu7MemCell {
|
||||
Eu7BasicNode node;
|
||||
std::string text;
|
||||
double value1 = 0.0;
|
||||
double value2 = 0.0;
|
||||
std::optional<std::string> track_name;
|
||||
};
|
||||
|
||||
struct Eu7EventLauncherCondition {
|
||||
std::string memcell_name;
|
||||
std::string compare_text;
|
||||
double compare_value1 = 0.0;
|
||||
double compare_value2 = 0.0;
|
||||
int check_mask = 0;
|
||||
};
|
||||
|
||||
struct Eu7EventLauncher {
|
||||
Eu7BasicNode node;
|
||||
glm::dvec3 location{};
|
||||
double radius_squared = 0.0;
|
||||
std::string activation_key_raw;
|
||||
int activation_key = 0;
|
||||
double delta_time = -1.0;
|
||||
std::string event1_name;
|
||||
std::string event2_name;
|
||||
std::optional<Eu7EventLauncherCondition> condition;
|
||||
bool train_triggered = false;
|
||||
int launch_hour = -1;
|
||||
int launch_minute = -1;
|
||||
};
|
||||
|
||||
struct Eu7Dynamic {
|
||||
Eu7BasicNode node;
|
||||
std::string data_folder;
|
||||
std::string skin_file;
|
||||
std::string mmd_file;
|
||||
std::string track_name;
|
||||
double offset = -1.0;
|
||||
std::string driver_type;
|
||||
int coupling = 3;
|
||||
std::string coupling_raw = "3";
|
||||
std::string coupling_params;
|
||||
float velocity = 0.f;
|
||||
int load_count = 0;
|
||||
std::string load_type;
|
||||
std::optional<std::string> destination;
|
||||
std::optional<std::size_t> trainset_index;
|
||||
};
|
||||
|
||||
struct Eu7Sound {
|
||||
Eu7BasicNode node;
|
||||
glm::dvec3 location{};
|
||||
std::string wav_file;
|
||||
};
|
||||
|
||||
enum class Eu7EventType : std::uint8_t {
|
||||
AddValues,
|
||||
UpdateValues,
|
||||
CopyValues,
|
||||
GetValues,
|
||||
PutValues,
|
||||
Whois,
|
||||
LogValues,
|
||||
Multiple,
|
||||
Switch,
|
||||
TrackVel,
|
||||
Sound,
|
||||
Texture,
|
||||
Animation,
|
||||
Lights,
|
||||
Voltage,
|
||||
Visible,
|
||||
Friction,
|
||||
Message,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
struct Eu7Event {
|
||||
std::string name;
|
||||
Eu7EventType type = Eu7EventType::Unknown;
|
||||
double delay = 0.0;
|
||||
std::vector<std::string> targets;
|
||||
double delay_random = 0.0;
|
||||
double delay_departure = 0.0;
|
||||
bool ignored = false;
|
||||
bool passive = false;
|
||||
std::vector<std::pair<std::string, std::string>> payload;
|
||||
};
|
||||
|
||||
struct Eu7Trainset {
|
||||
std::string name;
|
||||
std::string track;
|
||||
float offset = 0.f;
|
||||
float velocity = 0.f;
|
||||
std::unordered_map<std::string, std::string> assignment;
|
||||
std::vector<std::size_t> vehicle_indices;
|
||||
std::vector<int> couplings;
|
||||
std::size_t driver_index = static_cast<std::size_t>( -1 );
|
||||
};
|
||||
|
||||
struct Eu7IncludePlacement {
|
||||
std::uint8_t origin_x_param = 0;
|
||||
std::uint8_t origin_y_param = 0;
|
||||
std::uint8_t origin_z_param = 0;
|
||||
std::uint8_t rotation_y_param = 0;
|
||||
|
||||
[[nodiscard]] bool
|
||||
empty() const noexcept {
|
||||
return origin_x_param == 0 && origin_y_param == 0 && origin_z_param == 0 &&
|
||||
rotation_y_param == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Eu7Include {
|
||||
std::uint32_t source_line = 0;
|
||||
std::string source_path;
|
||||
std::string binary_path;
|
||||
std::vector<std::string> parameters;
|
||||
Eu7TransformContext site_transform;
|
||||
};
|
||||
|
||||
struct Eu7Scene {
|
||||
std::vector<Eu7Track> tracks;
|
||||
std::vector<Eu7Traction> traction;
|
||||
std::vector<Eu7TractionPowerSource> power_sources;
|
||||
std::vector<Eu7Shape> shapes;
|
||||
std::vector<Eu7Shape> terrain_shapes;
|
||||
std::vector<Eu7Lines> lines;
|
||||
std::vector<Eu7Model> models;
|
||||
std::vector<Eu7MemCell> memcells;
|
||||
std::vector<Eu7EventLauncher> event_launchers;
|
||||
std::vector<Eu7Dynamic> dynamics;
|
||||
std::vector<Eu7Sound> sounds;
|
||||
std::vector<Eu7Trainset> trainsets;
|
||||
std::vector<Eu7Event> events;
|
||||
std::uint32_t first_init_count = 0;
|
||||
};
|
||||
|
||||
// Wpis indeksu PACK: sekcja 1 km x 1 km -> offset w payload chunku PACK.
|
||||
struct Eu7PackIndexEntry {
|
||||
std::uint16_t row = 0;
|
||||
std::uint16_t column = 0;
|
||||
std::uint32_t model_count = 0;
|
||||
std::uint64_t pack_offset = 0;
|
||||
};
|
||||
|
||||
// Stan inkrementalnego odczytu sekcji PACK z istream (po seek_pack_section).
|
||||
struct Eu7PackSectionCursor {
|
||||
std::uint32_t solo_remaining { 0 };
|
||||
std::uint32_t inst_remaining { 0 };
|
||||
std::uint32_t models_read { 0 };
|
||||
std::uint32_t model_total { 0 };
|
||||
std::uint8_t section_format { 0 };
|
||||
bool header_parsed { false };
|
||||
};
|
||||
|
||||
struct Eu7PackCatalog {
|
||||
std::vector<Eu7PackIndexEntry> entries;
|
||||
std::unordered_map<std::uint32_t, std::size_t> index_by_section;
|
||||
std::uint64_t pack_payload_size = 0;
|
||||
};
|
||||
|
||||
struct Eu7Module {
|
||||
std::vector<Eu7Include> includes;
|
||||
Eu7Scene scene;
|
||||
Eu7IncludePlacement include_placement;
|
||||
Eu7PackCatalog pack_catalog;
|
||||
std::vector<Eu7ModelPrototype> model_prototypes;
|
||||
std::vector<std::string> strings;
|
||||
std::string source_path;
|
||||
std::uint64_t pack_payload_offset = 0;
|
||||
std::uint32_t version = 0;
|
||||
bool has_terrain_chunk = false;
|
||||
bool has_pack_chunk = false;
|
||||
};
|
||||
|
||||
} // namespace scene::eu7
|
||||
@@ -359,6 +359,21 @@ basic_cell::insert( TAnimModel *Instance ) {
|
||||
|
||||
m_active = true;
|
||||
|
||||
if( Instance->m_eu7_pack && Instance->m_instanceable ) {
|
||||
m_instancesopaque.emplace_back( Instance );
|
||||
if( Instance->Model() != nullptr ) {
|
||||
instance_bucket_key key;
|
||||
key.pModel = Instance->Model();
|
||||
auto const *mat = Instance->Material();
|
||||
if( mat != nullptr ) {
|
||||
for( int i = 0; i < 5; ++i ) { key.skins[ i ] = mat->replacable_skins[ i ]; }
|
||||
}
|
||||
m_instancebuckets_opaque[ key ].emplace_back( Instance );
|
||||
}
|
||||
enclose_area( Instance );
|
||||
return;
|
||||
}
|
||||
|
||||
auto const flags = Instance->Flags();
|
||||
auto alpha =
|
||||
( Instance->Material() != nullptr ?
|
||||
|
||||
@@ -74,11 +74,22 @@ struct node_data {
|
||||
std::string type;
|
||||
};
|
||||
|
||||
class shape_node;
|
||||
class lines_node;
|
||||
|
||||
namespace eu7 {
|
||||
struct Eu7Shape;
|
||||
struct Eu7Lines;
|
||||
shape_node build_shape_node( Eu7Shape const &Source );
|
||||
lines_node build_lines_node( Eu7Lines const &Source );
|
||||
} // namespace eu7
|
||||
|
||||
// holds unique piece of geometry, covered with single material
|
||||
class shape_node
|
||||
{
|
||||
|
||||
friend class basic_region; // region might want to modify node content when it's being inserted
|
||||
friend shape_node eu7::build_shape_node( eu7::Eu7Shape const & );
|
||||
|
||||
public:
|
||||
// types
|
||||
@@ -176,6 +187,7 @@ shape_node::data() const {
|
||||
class lines_node
|
||||
{
|
||||
friend class basic_region; // region might want to modify node content when it's being inserted
|
||||
friend lines_node eu7::build_lines_node( eu7::Eu7Lines const & );
|
||||
|
||||
public:
|
||||
// types
|
||||
|
||||
Reference in New Issue
Block a user