2V2-e.5b: feed native map height and light into game render
This commit is contained in:
@@ -10,7 +10,8 @@ if(NOT TARGET mtpython)
|
||||
list(FILTER MT_PLATFORM_SOURCES EXCLUDE REGEX "/ScriptLib/")
|
||||
endif()
|
||||
add_library(port_platform STATIC ${MT_PLATFORM_SOURCES})
|
||||
target_link_libraries(port_platform PUBLIC port_logic PRIVATE mt3p::freetype xrender::libgr2)
|
||||
target_link_libraries(port_platform PUBLIC port_logic PRIVATE mt3p::freetype xrender::libgr2 xrender::formats)
|
||||
target_include_directories(port_platform PRIVATE ${CMAKE_SOURCE_DIR}/formats)
|
||||
foreach(src IN LISTS MT_PLATFORM_SOURCES)
|
||||
mt_port_apply_defines(${src})
|
||||
endforeach()
|
||||
|
||||
@@ -206,6 +206,12 @@ void init_2v0_gameplay_stubs();
|
||||
|
||||
namespace PythonBoot
|
||||
{
|
||||
std::string CurrentMapName()
|
||||
{
|
||||
if (!g_game_singletons)
|
||||
return {};
|
||||
return CPythonBackground::Instance().GetWarpMapName();
|
||||
}
|
||||
|
||||
bool IsRunning()
|
||||
{
|
||||
|
||||
@@ -36,6 +36,8 @@ bool IsRunning();
|
||||
bool RunMainScript(const char* lpCmdLine, std::string* error);
|
||||
bool IsAppLooping();
|
||||
bool AppFrame();
|
||||
// Native CPythonBackground map selection, in the local coordinate frame of RenderGame.
|
||||
std::string CurrentMapName();
|
||||
|
||||
// Used by the platform CPythonApplication: Loop() calls AppLoopWait() before each Process() and stops
|
||||
// when it returns false; Exit()/Abort() call AppRequestQuit() (40250: PostQuitMessage).
|
||||
|
||||
@@ -3,13 +3,81 @@
|
||||
// (docs/PORT-PLAN.md).
|
||||
#include "GameLib/StdAfx.h"
|
||||
#include "GameLib/MapManager.h"
|
||||
#include "EterPack/EterPackManager.h"
|
||||
#include "EterBase/MappedFile.h"
|
||||
#include "EterLib/StateManager.h"
|
||||
#include "GameLib/MapUtil.h"
|
||||
|
||||
#include <map_setting.h>
|
||||
#include <terrain_files.h>
|
||||
#include <terrain_mesh.h>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "../../PlatformStub.h"
|
||||
|
||||
auto CMapManager::GetHeight(float, float) -> float
|
||||
namespace {
|
||||
// PORT: The Godot terrain adapter owns drawing until CMapOutdoor is ported. This
|
||||
// pack-backed state supplies its 40250 callers with the same height and .msenv
|
||||
// data during that transition; it is not a CMapOutdoor port.
|
||||
struct NativeMapState {
|
||||
fmt::MapSetting setting;
|
||||
std::string name;
|
||||
std::map<std::pair<int, int>, fmt::HeightMap> heights;
|
||||
bool ready = false;
|
||||
};
|
||||
std::map<const CMapManager *, NativeMapState> s_native_maps;
|
||||
|
||||
bool pack_bytes(const std::string &name, std::string &out)
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<float>();
|
||||
CMappedFile file;
|
||||
LPCVOID data = nullptr;
|
||||
if (!CEterPackManager::Instance().Get(file, name.c_str(), &data))
|
||||
return false;
|
||||
out.assign(static_cast<const char *>(data), file.Size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load_height_tile(NativeMapState &state, int tx, int ty)
|
||||
{
|
||||
if (tx < 0 || ty < 0 || tx >= state.setting.map_size_x || ty >= state.setting.map_size_y)
|
||||
return false;
|
||||
const auto key = std::make_pair(tx, ty);
|
||||
if (state.heights.find(key) != state.heights.end())
|
||||
return true;
|
||||
char tile[16];
|
||||
snprintf(tile, sizeof(tile), "%03d%03d", tx, ty);
|
||||
std::string bytes;
|
||||
if (!pack_bytes(state.name + "/" + tile + "/height.raw", bytes) || bytes.size() != 131 * 131 * 2)
|
||||
return false;
|
||||
fmt::HeightMap map;
|
||||
map.raw.resize(131 * 131);
|
||||
for (size_t i = 0; i < map.raw.size(); ++i)
|
||||
map.raw[i] = static_cast<unsigned char>(bytes[i * 2]) |
|
||||
(static_cast<unsigned char>(bytes[i * 2 + 1]) << 8);
|
||||
state.heights.emplace(key, std::move(map));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
auto CMapManager::GetHeight(float fx, float fy) -> float
|
||||
{
|
||||
auto found = s_native_maps.find(this);
|
||||
if (found == s_native_maps.end() || !found->second.ready)
|
||||
return 0.0f;
|
||||
NativeMapState &state = found->second;
|
||||
const double x = fx, y = std::abs(double(fy));
|
||||
if (x < 0 || y < 0)
|
||||
return 0.0f;
|
||||
const int tx = int(x / 25600.0), ty = int(y / 25600.0);
|
||||
if (!load_height_tile(state, tx, ty))
|
||||
return 0.0f;
|
||||
const float height = float(fmt::terrain_height_at(state.heights.at({tx, ty}), x - tx * 25600.0,
|
||||
y - ty * 25600.0, state.setting.height_scale));
|
||||
return height;
|
||||
}
|
||||
|
||||
CMapManager::CMapManager() : mc_pcurEnvironmentData(NULL)
|
||||
@@ -22,12 +90,51 @@ CMapManager::CMapManager() : mc_pcurEnvironmentData(NULL)
|
||||
|
||||
CMapManager::~CMapManager()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void CMapManager::Initialize()
|
||||
{
|
||||
mc_pcurEnvironmentData = NULL;
|
||||
__LoadMapInfoVector();
|
||||
}
|
||||
|
||||
void CMapManager::Create()
|
||||
{
|
||||
// PORT: CMapOutdoor is still rendered by Metin2World, not this native object.
|
||||
}
|
||||
|
||||
void CMapManager::Destroy()
|
||||
{
|
||||
Clear();
|
||||
for (auto &entry : m_EnvironmentDataMap)
|
||||
delete entry.second;
|
||||
m_EnvironmentDataMap.clear();
|
||||
}
|
||||
|
||||
void CMapManager::__LoadMapInfoVector()
|
||||
{
|
||||
std::string bytes;
|
||||
if (!pack_bytes(m_stAtlasInfoFileName, bytes) && !pack_bytes("AtlasInfo.txt", bytes))
|
||||
return;
|
||||
std::istringstream lines(bytes);
|
||||
std::string line;
|
||||
m_kVct_kMapInfo.clear();
|
||||
while (std::getline(lines, line)) {
|
||||
std::istringstream fields(line);
|
||||
TMapInfo info{};
|
||||
if (!(fields >> info.m_strName >> info.m_dwBaseX >> info.m_dwBaseY >> info.m_dwSizeX >> info.m_dwSizeY))
|
||||
continue;
|
||||
info.m_dwEndX = info.m_dwBaseX + info.m_dwSizeX * 25600;
|
||||
info.m_dwEndY = info.m_dwBaseY + info.m_dwSizeY * 25600;
|
||||
m_kVct_kMapInfo.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
auto CMapManager::Clear() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
s_native_maps.erase(this);
|
||||
mc_pcurEnvironmentData = NULL;
|
||||
}
|
||||
|
||||
auto CMapManager::AllocMap() -> CMapBase *
|
||||
@@ -36,10 +143,37 @@ auto CMapManager::AllocMap() -> CMapBase *
|
||||
return mt_platform_stub_return<CMapBase *>();
|
||||
}
|
||||
|
||||
auto CMapManager::LoadMap(const std::string &, float, float, float) -> bool
|
||||
auto CMapManager::LoadMap(const std::string &name, float x, float y, float) -> bool
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
return mt_platform_stub_return<bool>();
|
||||
NativeMapState next;
|
||||
next.name = name;
|
||||
std::string bytes, error;
|
||||
if (!pack_bytes(name + "/setting.txt", bytes) || !fmt::parse_map_setting(bytes, next.setting, &error)) {
|
||||
TraceError("native map setting failed: %s (%s)", name.c_str(), error.c_str());
|
||||
return false;
|
||||
}
|
||||
if (!load_height_tile(next, int(x / 25600.0f), int(y / 25600.0f))) {
|
||||
TraceError("native map height tile failed: %s (%d,%d)", name.c_str(), int(x / 25600.0f), int(y / 25600.0f));
|
||||
return false;
|
||||
}
|
||||
for (auto &entry : m_EnvironmentDataMap)
|
||||
delete entry.second;
|
||||
m_EnvironmentDataMap.clear();
|
||||
mc_pcurEnvironmentData = NULL;
|
||||
if (!next.setting.environment.empty()) {
|
||||
auto env = std::make_unique<TEnvironmentData>();
|
||||
Environment_Init(*env);
|
||||
const std::string env_path = "d:/ymir work/environment/" + next.setting.environment;
|
||||
if (Environment_Load(*env, env_path.c_str())) {
|
||||
m_EnvironmentDataMap[0] = env.release();
|
||||
mc_pcurEnvironmentData = m_EnvironmentDataMap[0];
|
||||
} else {
|
||||
TraceError("native map environment failed: %s", env_path.c_str());
|
||||
}
|
||||
}
|
||||
next.ready = true;
|
||||
s_native_maps[this] = std::move(next);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CMapManager::isPhysicalCollision(const D3DXVECTOR3 &) -> bool
|
||||
@@ -74,19 +208,23 @@ auto CMapManager::isAttrOn(int, int, BYTE) -> bool
|
||||
|
||||
auto CMapManager::BeginEnvironment() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!IsMapReady() || !mc_pcurEnvironmentData)
|
||||
return;
|
||||
STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, TRUE);
|
||||
STATEMANAGER.SetMaterial(&mc_pcurEnvironmentData->Material);
|
||||
STATEMANAGER.SetLight(0, &mc_pcurEnvironmentData->DirLights[ENV_DIRLIGHT_BACKGROUND]);
|
||||
// PORT: RecordingDevice stores SetLight but has no D3D LightEnable slot yet.
|
||||
}
|
||||
|
||||
auto CMapManager::EndEnvironment() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (IsMapReady() && mc_pcurEnvironmentData)
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING);
|
||||
}
|
||||
|
||||
// 40250 MapManager.cpp:144 (CPythonApplication::OnCameraUpdate); m_pkMap stays null until LoadMap is ported.
|
||||
// PORT: MapOutdoor is not allocated yet; report the pack-backed transition map's readiness.
|
||||
bool CMapManager::IsMapReady()
|
||||
{
|
||||
if (!m_pkMap)
|
||||
return false;
|
||||
|
||||
return m_pkMap->IsReady();
|
||||
auto found = s_native_maps.find(this);
|
||||
return found != s_native_maps.end() && found->second.ready;
|
||||
}
|
||||
|
||||
@@ -3,27 +3,31 @@
|
||||
// (docs/PORT-PLAN.md).
|
||||
#include "UserInterface/StdAfx.h"
|
||||
#include "UserInterface/PythonBackground.h"
|
||||
#include "EterLib/StateManager.h"
|
||||
|
||||
#include "../../PlatformStub.h"
|
||||
|
||||
auto CPythonBackground::Destroy() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
CMapManager::Destroy();
|
||||
m_bVisibleGuildArea = FALSE;
|
||||
}
|
||||
|
||||
auto CPythonBackground::Create() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
CMapManager::Create();
|
||||
}
|
||||
|
||||
auto CPythonBackground::GlobalPositionToLocalPosition(LONG &, LONG &) -> void
|
||||
auto CPythonBackground::GlobalPositionToLocalPosition(LONG &x, LONG &y) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
x -= m_dwBaseX;
|
||||
y -= m_dwBaseY;
|
||||
}
|
||||
|
||||
auto CPythonBackground::LocalPositionToGlobalPosition(LONG &, LONG &) -> void
|
||||
auto CPythonBackground::LocalPositionToGlobalPosition(LONG &x, LONG &y) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
x += m_dwBaseX;
|
||||
y += m_dwBaseY;
|
||||
}
|
||||
|
||||
auto CPythonBackground::RefreshShadowLevel() -> void
|
||||
@@ -61,24 +65,41 @@ auto CPythonBackground::DeleteTargetEffect(DWORD) -> void
|
||||
MT_PLATFORM_STUB();
|
||||
}
|
||||
|
||||
auto CPythonBackground::Warp(DWORD, DWORD) -> void
|
||||
auto CPythonBackground::Warp(DWORD x, DWORD y) -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (m_kVct_kMapInfo.empty())
|
||||
Initialize();
|
||||
auto found = std::find_if(m_kVct_kMapInfo.begin(), m_kVct_kMapInfo.end(),
|
||||
[x, y](const TMapInfo &info) {
|
||||
return x >= info.m_dwBaseX && x < info.m_dwEndX &&
|
||||
y >= info.m_dwBaseY && y < info.m_dwEndY;
|
||||
});
|
||||
if (found == m_kVct_kMapInfo.end()) {
|
||||
TraceError("NOT_FOUND_GLOBAL_POSITION(%u, %u)", x, y);
|
||||
return;
|
||||
}
|
||||
if (!LoadMap(found->m_strName, float(x - found->m_dwBaseX),
|
||||
float(y - found->m_dwBaseY), 0.0f)) {
|
||||
TraceError("CPythonBackground::Warp map load failed: %s", found->m_strName.c_str());
|
||||
return;
|
||||
}
|
||||
m_dwBaseX = found->m_dwBaseX;
|
||||
m_dwBaseY = found->m_dwBaseY;
|
||||
m_strMapName = found->m_strName;
|
||||
}
|
||||
|
||||
auto CPythonBackground::GetWarpMapName() -> const char *
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
// PORT: Keep the not-yet-ported map lookup safe for GamePhase's ShowMapName call.
|
||||
return "";
|
||||
return m_strMapName.c_str();
|
||||
}
|
||||
|
||||
CPythonBackground::CPythonBackground()
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
// PORT: the one member the stand-in's ported GetFarClip reads (PythonBackground.cpp:210); the
|
||||
// view distance sets and Initialize() come with the unit.
|
||||
m_eViewDistanceNum=0;
|
||||
m_dwBaseX=0;
|
||||
m_dwBaseY=0;
|
||||
m_bVisibleGuildArea=FALSE;
|
||||
m_iXMasTreeGrade=0;
|
||||
}
|
||||
|
||||
CPythonBackground::~CPythonBackground()
|
||||
@@ -166,10 +187,22 @@ auto CPythonBackground::RenderWater() -> void
|
||||
|
||||
auto CPythonBackground::SetBackgroundDirLight() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!IsMapReady() || !mc_pcurEnvironmentData)
|
||||
return;
|
||||
STATEMANAGER.SetLight(0, &mc_pcurEnvironmentData->DirLights[ENV_DIRLIGHT_BACKGROUND]);
|
||||
}
|
||||
|
||||
auto CPythonBackground::SetCharacterDirLight() -> void
|
||||
{
|
||||
MT_PLATFORM_STUB();
|
||||
if (!IsMapReady() || !mc_pcurEnvironmentData)
|
||||
return;
|
||||
STATEMANAGER.SetLight(0, &mc_pcurEnvironmentData->DirLights[ENV_DIRLIGHT_CHARACTER]);
|
||||
}
|
||||
|
||||
void CPythonBackground::Initialize()
|
||||
{
|
||||
std::string atlas(LocaleService_GetLocalePath());
|
||||
atlas += "/AtlasInfo.txt";
|
||||
SetAtlasInfoFileName(atlas.c_str());
|
||||
CMapManager::Initialize();
|
||||
}
|
||||
|
||||
@@ -224,6 +224,10 @@ Array Metin2PythonHost::render3d_draws() {
|
||||
return out;
|
||||
}
|
||||
|
||||
String Metin2PythonHost::current_map_name() {
|
||||
return String::utf8(PythonBoot::CurrentMapName().c_str());
|
||||
}
|
||||
|
||||
void Metin2PythonHost::stop() {
|
||||
PythonBoot::Stop();
|
||||
}
|
||||
@@ -257,6 +261,7 @@ void Metin2PythonHost::ui_update() {}
|
||||
Array Metin2PythonHost::ui_render_commands() { return Array(); }
|
||||
Ref<Image> Metin2PythonHost::memory_texture(const String &, int64_t) { return Ref<Image>(); }
|
||||
Array Metin2PythonHost::render3d_draws() { return Array(); }
|
||||
String Metin2PythonHost::current_map_name() { return String(); }
|
||||
|
||||
void Metin2PythonHost::stop() {
|
||||
}
|
||||
@@ -280,6 +285,7 @@ void Metin2PythonHost::_bind_methods() {
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("memory_texture", "name", "known_revision"),
|
||||
&Metin2PythonHost::memory_texture, DEFVAL(-1));
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("render3d_draws"), &Metin2PythonHost::render3d_draws);
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("current_map_name"), &Metin2PythonHost::current_map_name);
|
||||
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("stop"), &Metin2PythonHost::stop);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
// "view", "proj" (PackedFloat32Array, D3D row-vector layout), vertex arrays, "indices", "texture0"
|
||||
// and the blend/depth/cull/lighting state.
|
||||
static godot::Array render3d_draws();
|
||||
static godot::String current_map_name();
|
||||
static void stop();
|
||||
|
||||
protected:
|
||||
|
||||
Reference in New Issue
Block a user