452 lines
19 KiB
C++
452 lines
19 KiB
C++
#include "environment_builder.h"
|
||
|
||
#include "asset_io.h"
|
||
#include "dxt.h"
|
||
|
||
#include <godot_cpp/classes/environment.hpp>
|
||
#include <godot_cpp/classes/image.hpp>
|
||
#include <godot_cpp/classes/image_texture.hpp>
|
||
#include <godot_cpp/classes/procedural_sky_material.hpp>
|
||
#include <godot_cpp/classes/shader.hpp>
|
||
#include <godot_cpp/classes/shader_material.hpp>
|
||
#include <godot_cpp/classes/sky.hpp>
|
||
#include <godot_cpp/classes/texture2d.hpp>
|
||
#include <godot_cpp/core/object.hpp>
|
||
|
||
#include <m2_coord.h>
|
||
|
||
#include <algorithm>
|
||
|
||
using namespace godot;
|
||
|
||
namespace mtgodot {
|
||
|
||
namespace {
|
||
|
||
Color rgba(const fmt::Rgba &c) { return Color(c[0], c[1], c[2], c[3]); }
|
||
float luma(const fmt::Rgba &c) { return 0.2126f * c[0] + 0.7152f * c[1] + 0.0722f * c[2]; }
|
||
|
||
const char *SRC_SKYBOX = R"(shader_type sky;
|
||
render_mode use_debanding;
|
||
|
||
uniform sampler2D front_tex : source_color, filter_linear_mipmap_anisotropic;
|
||
uniform sampler2D back_tex : source_color, filter_linear_mipmap_anisotropic;
|
||
uniform sampler2D left_tex : source_color, filter_linear_mipmap_anisotropic;
|
||
uniform sampler2D right_tex : source_color, filter_linear_mipmap_anisotropic;
|
||
uniform sampler2D top_tex : source_color, filter_linear_mipmap_anisotropic;
|
||
uniform sampler2D bottom_tex : source_color, filter_linear_mipmap_anisotropic;
|
||
uniform bool has_front = false;
|
||
uniform bool has_back = false;
|
||
uniform bool has_left = false;
|
||
uniform bool has_right = false;
|
||
uniform bool has_top = false;
|
||
uniform bool has_bottom = false;
|
||
uniform vec4 gradient_top = vec4(0.2, 0.3, 0.6, 1.0);
|
||
uniform vec4 gradient_horizon = vec4(0.5, 0.6, 0.8, 1.0);
|
||
uniform vec4 gradient_bottom = vec4(0.2, 0.2, 0.25, 1.0);
|
||
uniform sampler2D cloud_tex : source_color, repeat_enable, filter_linear_mipmap_anisotropic;
|
||
uniform bool has_cloud = false;
|
||
uniform vec2 cloud_scale = vec2(2000.0, 2000.0);
|
||
uniform float cloud_height = 300.0;
|
||
uniform vec2 cloud_texture_scale = vec2(4.0, 4.0);
|
||
uniform vec2 cloud_speed = vec2(0.001, 0.001);
|
||
uniform vec4 cloud_tint = vec4(0.0, 0.0, 0.0, 0.0);
|
||
|
||
vec3 gradient_color(vec3 direction) {
|
||
float height = clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);
|
||
if (height < 0.5)
|
||
return mix(gradient_bottom.rgb, gradient_horizon.rgb, height * 2.0);
|
||
return mix(gradient_horizon.rgb, gradient_top.rgb, (height - 0.5) * 2.0);
|
||
}
|
||
|
||
vec3 apply_cloud(vec3 color, vec3 direction) {
|
||
if (!has_cloud || direction.y <= 0.001 || cloud_height <= 0.0 ||
|
||
cloud_scale.x <= 0.001 || cloud_scale.y <= 0.001)
|
||
return color;
|
||
|
||
// ClientVS22 renders a finite horizontal quad at camera.z + CloudHeight.
|
||
// After the Metin2 -> Godot conversion this is camera.y + height. The
|
||
// source quad maps source +Y -> U=0 and source +X -> V=1; source Y is
|
||
// Godot -Z, so keep that orientation here.
|
||
float distance_to_cloud = cloud_height / direction.y;
|
||
vec2 plane = direction.xz * distance_to_cloud;
|
||
if (abs(plane.x) > cloud_scale.x || abs(plane.y) > cloud_scale.y)
|
||
return color;
|
||
vec2 uv = vec2(
|
||
0.5 + 0.5 * plane.y / cloud_scale.y,
|
||
0.5 + 0.5 * plane.x / cloud_scale.x);
|
||
uv = fract(uv * cloud_texture_scale + TIME * cloud_speed);
|
||
|
||
// SkyBox.cpp uses MODULATEINVALPHA_ADDCOLOR followed by ONE /
|
||
// INVSRCCOLOR blending: cloud.rgb + sky * (1 - cloud.rgb).
|
||
vec4 texel = texture(cloud_tex, uv);
|
||
vec3 cloud_rgb = clamp(texel.rgb * (1.0 - cloud_tint.a) + cloud_tint.rgb,
|
||
0.0, 1.0);
|
||
return cloud_rgb + color * (1.0 - cloud_rgb);
|
||
}
|
||
|
||
void sky() {
|
||
vec3 direction = normalize(EYEDIR);
|
||
vec3 color = gradient_color(direction);
|
||
float ax = abs(direction.x);
|
||
float ay = abs(direction.y);
|
||
float az = abs(direction.z);
|
||
vec2 uv;
|
||
|
||
// ClientVS22 uses Metin2 Z-up faces. After x,z,-y conversion:
|
||
// front=+Z, back=-Z, left=+X, right=-X, top=+Y, bottom=-Y.
|
||
if (az >= ax && az >= ay) {
|
||
if (direction.z > 0.0) {
|
||
uv = vec2(0.5 - 0.5 * direction.x / az,
|
||
0.5 - 0.5 * direction.y / az);
|
||
if (has_front)
|
||
color = texture(front_tex, uv).rgb;
|
||
} else {
|
||
uv = vec2(0.5 + 0.5 * direction.x / az,
|
||
0.5 - 0.5 * direction.y / az);
|
||
if (has_back)
|
||
color = texture(back_tex, uv).rgb;
|
||
}
|
||
} else if (ax >= ay) {
|
||
if (direction.x > 0.0) {
|
||
uv = vec2(0.5 + 0.5 * direction.z / ax,
|
||
0.5 - 0.5 * direction.y / ax);
|
||
if (has_left)
|
||
color = texture(left_tex, uv).rgb;
|
||
} else {
|
||
uv = vec2(0.5 - 0.5 * direction.z / ax,
|
||
0.5 - 0.5 * direction.y / ax);
|
||
if (has_right)
|
||
color = texture(right_tex, uv).rgb;
|
||
}
|
||
} else if (direction.y > 0.0) {
|
||
uv = vec2(0.5 - 0.5 * direction.x / ay,
|
||
0.5 - 0.5 * direction.z / ay);
|
||
if (has_top)
|
||
color = texture(top_tex, uv).rgb;
|
||
} else {
|
||
uv = vec2(0.5 - 0.5 * direction.x / ay,
|
||
0.5 - 0.5 * direction.z / ay);
|
||
if (has_bottom)
|
||
color = texture(bottom_tex, uv).rgb;
|
||
}
|
||
COLOR = apply_cloud(color, direction);
|
||
}
|
||
)";
|
||
|
||
Ref<ImageTexture> solid_texture(const Color &color) {
|
||
PackedByteArray pixels;
|
||
pixels.resize(4);
|
||
uint8_t *p = pixels.ptrw();
|
||
p[0] = (uint8_t)std::round(std::clamp(color.r, 0.0f, 1.0f) * 255.0f);
|
||
p[1] = (uint8_t)std::round(std::clamp(color.g, 0.0f, 1.0f) * 255.0f);
|
||
p[2] = (uint8_t)std::round(std::clamp(color.b, 0.0f, 1.0f) * 255.0f);
|
||
p[3] = (uint8_t)std::round(std::clamp(color.a, 0.0f, 1.0f) * 255.0f);
|
||
Ref<godot::Image> image = godot::Image::create_from_data(
|
||
1, 1, false, godot::Image::FORMAT_RGBA8, pixels);
|
||
image->generate_mipmaps();
|
||
return ImageTexture::create_from_image(image);
|
||
}
|
||
|
||
Ref<ImageTexture> load_texture(const std::string &name, const fmt::AssetResolver *resolver) {
|
||
if (name.empty() || resolver == nullptr)
|
||
return Ref<ImageTexture>();
|
||
const std::string path = resolver->resolve(name, nullptr);
|
||
if (path.empty())
|
||
return Ref<ImageTexture>();
|
||
Ref<godot::Image> image;
|
||
const String godot_path(path.c_str());
|
||
if (godot_path.get_extension().to_lower() == "dds") {
|
||
const mtgodot::Image decoded = mtgodot::dds_from_file(godot_path);
|
||
if (!decoded.ok())
|
||
return Ref<ImageTexture>();
|
||
PackedByteArray pixels;
|
||
pixels.resize((int64_t)decoded.rgba.size());
|
||
std::copy(decoded.rgba.begin(), decoded.rgba.end(), pixels.ptrw());
|
||
image = godot::Image::create_from_data(decoded.w, decoded.h, false,
|
||
godot::Image::FORMAT_RGBA8, pixels);
|
||
} else {
|
||
// Image::load_from_file cannot open the pack:// virtual filesystem.
|
||
if (godot_path.begins_with("pack://")) {
|
||
PackedByteArray bytes = mtgodot::read_file(godot_path);
|
||
image.instantiate();
|
||
if (godot_path.get_extension().to_lower() == "tga")
|
||
image->load_tga_from_buffer(bytes);
|
||
else if (godot_path.get_extension().to_lower() == "png")
|
||
image->load_png_from_buffer(bytes);
|
||
else if (godot_path.get_extension().to_lower() == "jpg")
|
||
image->load_jpg_from_buffer(bytes);
|
||
} else {
|
||
image = godot::Image::load_from_file(godot_path);
|
||
}
|
||
}
|
||
if (image.is_null() || image->is_empty())
|
||
return Ref<ImageTexture>();
|
||
image->generate_mipmaps();
|
||
return ImageTexture::create_from_image(image);
|
||
}
|
||
|
||
} // namespace
|
||
|
||
EnvNodes apply_environment(const fmt::Environment &env, Node *parent,
|
||
const fmt::AssetResolver *resolver) {
|
||
EnvNodes out;
|
||
|
||
// --- DirectionalLight (Background) ---
|
||
out.sun = Object::cast_to<DirectionalLight3D>(parent->get_node_or_null(NodePath("Sun")));
|
||
if (!out.sun) {
|
||
out.sun = memnew(DirectionalLight3D);
|
||
out.sun->set_name("Sun");
|
||
parent->add_child(out.sun);
|
||
}
|
||
out.sun->set_cull_mask(1u << 0); // background / terrain layer
|
||
out.sun->set_visible(env.dir_light.bg_enable);
|
||
// .msenv Direction 是 Metin2 Z-up 向量(光传播方向)。
|
||
fmt::m2coord::Vec3 d = fmt::m2coord::direction_to_godot(
|
||
env.dir_light.direction[0], env.dir_light.direction[1], env.dir_light.direction[2]);
|
||
Vector3 fwd(d.x, d.y, d.z);
|
||
if (fwd.length() < 1e-4f)
|
||
fwd = Vector3(-0.4f, -0.7f, -0.55f);
|
||
fwd.normalize();
|
||
// Godot 光沿自身 -Z 照射 -> basis 的 -Z = fwd
|
||
Basis b = Basis::looking_at(fwd, Vector3(0, 1, 0));
|
||
out.sun->set_transform(Transform3D(b, Vector3(0, 0, 0)));
|
||
if (env.dir_light.bg_enable || luma(env.dir_light.bg_diffuse) > 0.01f) {
|
||
out.sun->set_color(rgba(env.dir_light.bg_diffuse));
|
||
out.sun->set_param(Light3D::PARAM_ENERGY,
|
||
std::clamp(0.9f + luma(env.dir_light.bg_diffuse) * 0.4f, 0.7f, 1.6f));
|
||
}
|
||
out.sun->set_shadow(true);
|
||
out.sun->set_param(Light3D::PARAM_SHADOW_NORMAL_BIAS, 2.0f);
|
||
out.sun->set_param(Light3D::PARAM_SHADOW_BIAS, 0.06f);
|
||
out.sun->set_param(Light3D::PARAM_SHADOW_MAX_DISTANCE, 500.0f);
|
||
out.sun->set_param(Light3D::PARAM_SHADOW_SPLIT_1_OFFSET, 0.08f);
|
||
out.sun->set_param(Light3D::PARAM_SHADOW_SPLIT_2_OFFSET, 0.22f);
|
||
out.sun->set_param(Light3D::PARAM_SHADOW_SPLIT_3_OFFSET, 0.5f);
|
||
out.sun->set_shadow_mode(DirectionalLight3D::SHADOW_PARALLEL_4_SPLITS);
|
||
// 角色/物体的间接补光:暖色,能量取自 Material.Ambient
|
||
out.sun->set_param(Light3D::PARAM_SPECULAR, 0.4f);
|
||
|
||
// ClientVS22 keeps the character light separate from the background light
|
||
// (MapOutdoor::OnBeginEnvironment -> SpeedTree::SetLight). Godot's light
|
||
// cull mask is the closest exact scene-level equivalent: static world
|
||
// geometry is layer 1, Metin2Model geometry is layer 2.
|
||
out.character_light = Object::cast_to<DirectionalLight3D>(
|
||
parent->get_node_or_null(NodePath("CharacterLight")));
|
||
if (!out.character_light) {
|
||
out.character_light = memnew(DirectionalLight3D);
|
||
out.character_light->set_name("CharacterLight");
|
||
parent->add_child(out.character_light);
|
||
}
|
||
out.character_light->set_cull_mask(1u << 1);
|
||
out.character_light->set_visible(env.dir_light.ch_enable);
|
||
out.character_light->set_transform(Transform3D(b, Vector3(0, 0, 0)));
|
||
out.character_light->set_color(rgba(env.dir_light.ch_diffuse));
|
||
out.character_light->set_param(Light3D::PARAM_ENERGY,
|
||
std::clamp(0.9f + luma(env.dir_light.ch_diffuse) * 0.4f, 0.7f, 1.6f));
|
||
out.character_light->set_shadow(false);
|
||
out.character_light->set_param(Light3D::PARAM_SPECULAR, 0.4f);
|
||
|
||
// --- WorldEnvironment ---
|
||
out.world_env =
|
||
Object::cast_to<WorldEnvironment>(parent->get_node_or_null(NodePath("WorldEnv")));
|
||
if (!out.world_env) {
|
||
out.world_env = memnew(WorldEnvironment);
|
||
out.world_env->set_name("WorldEnv");
|
||
parent->add_child(out.world_env);
|
||
}
|
||
Ref<godot::Environment> e = out.world_env->get_environment();
|
||
if (e.is_null())
|
||
e.instantiate();
|
||
|
||
// 天空:SkyBox Gradient -> ProceduralSky 的三段色(zenith / horizon / ground)。
|
||
// 参考端的 texture mode 是独立六面 quad;Godot Sky shader 用 EYEDIR 采样
|
||
// 同一套六面图,坐标和 UV 依照 EterLib/SkyBox.cpp 的面顶点顺序转换。
|
||
int sky_face_count = 0;
|
||
bool sky_texture_applied = false;
|
||
bool sky_cloud_applied = false;
|
||
{
|
||
Ref<ProceduralSkyMaterial> psm;
|
||
psm.instantiate();
|
||
const auto &g = env.sky.gradient;
|
||
if (g.size() >= 2) {
|
||
psm->set_sky_top_color(rgba(g.front()));
|
||
psm->set_sky_horizon_color(rgba(g[g.size() / 2]));
|
||
psm->set_ground_horizon_color(rgba(g.back()));
|
||
Color gb = rgba(g.back());
|
||
psm->set_ground_bottom_color(Color(gb.r * 0.6f, gb.g * 0.6f, gb.b * 0.65f));
|
||
}
|
||
psm->set_sun_angle_max(6.0f);
|
||
Ref<Sky> sky;
|
||
sky.instantiate();
|
||
std::array<Ref<ImageTexture>, 6> faces;
|
||
std::array<bool, 6> face_loaded{};
|
||
for (int i = 0; i < 6; ++i) {
|
||
if (env.sky.face_textures[i].empty())
|
||
continue;
|
||
faces[i] = load_texture(env.sky.face_textures[i], resolver);
|
||
face_loaded[i] = faces[i].is_valid();
|
||
if (face_loaded[i])
|
||
++sky_face_count;
|
||
}
|
||
Ref<ImageTexture> cloud_texture;
|
||
if (!env.sky.cloud_texture.empty())
|
||
cloud_texture = load_texture(env.sky.cloud_texture, resolver);
|
||
const bool use_texture_sky = env.sky.texture_render_mode && sky_face_count > 0;
|
||
sky_texture_applied = use_texture_sky;
|
||
if (use_texture_sky) {
|
||
Ref<Shader> shader;
|
||
shader.instantiate();
|
||
shader->set_code(String(SRC_SKYBOX));
|
||
Ref<ShaderMaterial> material;
|
||
material.instantiate();
|
||
material->set_shader(shader);
|
||
Color fallback = g.empty() ? Color(0.35f, 0.45f, 0.7f, 1.0f) : rgba(g.front());
|
||
Color horizon = g.size() < 2 ? fallback : rgba(g[g.size() / 2]);
|
||
Color ground = g.empty() ? Color(0.2f, 0.2f, 0.25f, 1.0f) : rgba(g.back());
|
||
material->set_shader_parameter("gradient_top", fallback);
|
||
material->set_shader_parameter("gradient_horizon", horizon);
|
||
material->set_shader_parameter("gradient_bottom", ground);
|
||
static constexpr const char *kFaceParams[6] = {
|
||
"front_tex", "back_tex", "left_tex", "right_tex", "top_tex", "bottom_tex"};
|
||
static constexpr const char *kFaceFlags[6] = {
|
||
"has_front", "has_back", "has_left", "has_right", "has_top", "has_bottom"};
|
||
for (int i = 0; i < 6; ++i) {
|
||
if (!faces[i].is_valid())
|
||
faces[i] = solid_texture(i == 5 ? ground : horizon);
|
||
material->set_shader_parameter(kFaceParams[i], faces[i]);
|
||
material->set_shader_parameter(kFaceFlags[i], face_loaded[i]);
|
||
}
|
||
if (cloud_texture.is_valid()) {
|
||
material->set_shader_parameter("cloud_tex", cloud_texture);
|
||
material->set_shader_parameter("has_cloud", true);
|
||
material->set_shader_parameter("cloud_scale", Vector2(
|
||
std::max(env.sky.cloud_scale[0] * (float)fmt::m2coord::CM_TO_M, 0.01f),
|
||
std::max(env.sky.cloud_scale[1] * (float)fmt::m2coord::CM_TO_M, 0.01f)));
|
||
material->set_shader_parameter("cloud_height",
|
||
std::max(env.sky.cloud_height * (float)fmt::m2coord::CM_TO_M, 0.01f));
|
||
material->set_shader_parameter("cloud_texture_scale", Vector2(
|
||
env.sky.cloud_texture_scale[0], env.sky.cloud_texture_scale[1]));
|
||
material->set_shader_parameter("cloud_speed", Vector2(
|
||
env.sky.cloud_speed[0], env.sky.cloud_speed[1]));
|
||
Color tint = Color(0, 0, 0, 0);
|
||
if (!env.sky.cloud_color.empty())
|
||
tint = rgba(env.sky.cloud_color.front());
|
||
material->set_shader_parameter("cloud_tint", tint);
|
||
sky_cloud_applied = true;
|
||
}
|
||
sky->set_material(material);
|
||
} else {
|
||
sky->set_material(psm);
|
||
}
|
||
// Reference SkyBox scrolls clouds in its own renderer. Godot's procedural
|
||
// sky has no equivalent UV-speed control, but sky_cover preserves the real
|
||
// cloud asset and alpha/color instead of silently dropping CloudTextureFileName.
|
||
if (!env.sky.cloud_texture.empty() && !use_texture_sky) {
|
||
if (cloud_texture.is_valid()) {
|
||
psm->set_sky_cover(cloud_texture);
|
||
Color tint = Color(1, 1, 1, 1);
|
||
if (!env.sky.cloud_color.empty())
|
||
tint = rgba(env.sky.cloud_color.front());
|
||
psm->set_sky_cover_modulate(tint);
|
||
sky_cloud_applied = true;
|
||
}
|
||
}
|
||
e->set_sky(sky);
|
||
e->set_background(godot::Environment::BG_SKY);
|
||
}
|
||
|
||
// 环境光:Material.Ambient 定色 + 亮度(Metin2 用暖色环境光提亮阴影面)
|
||
e->set_ambient_source(godot::Environment::AMBIENT_SOURCE_COLOR);
|
||
Color amb = rgba(env.material.ambient);
|
||
e->set_ambient_light_color(amb);
|
||
e->set_ambient_light_energy(std::clamp(0.5f + luma(env.material.ambient) * 0.4f, 0.35f, 0.95f));
|
||
e->set_ambient_light_sky_contribution(0.35f);
|
||
// Emissive 当作全局轻微自发光提亮(避免死黑)
|
||
e->set_bg_energy_multiplier(1.0f);
|
||
|
||
// 雾:优先用 .msenv 的 NearDistance/FarDistance(cm,A1 = 5000/20000 -> 50/200m
|
||
// 太近,客户端 D3DFOG 实际按更大的世界尺度;乘一个系数放到远景轻霭区)。没给
|
||
// 距离就退回 foglevel 启发式。参考端是线性远景雾 + 天空同色,不是浓雾。
|
||
if (env.fog.enable) {
|
||
e->set_fog_enabled(true);
|
||
e->set_fog_light_color(rgba(env.fog.color));
|
||
e->set_fog_mode(godot::Environment::FOG_MODE_DEPTH);
|
||
float begin_m, end_m;
|
||
if (env.fog.near_distance > 1.0f && env.fog.far_distance > env.fog.near_distance) {
|
||
begin_m = env.fog.near_distance * (float)fmt::m2coord::CM_TO_M;
|
||
end_m = env.fog.far_distance * (float)fmt::m2coord::CM_TO_M;
|
||
// 客户端摄距比我们远:把近雾往后推一截,别糊住中景
|
||
begin_m = std::max(begin_m, 120.0f);
|
||
end_m = std::max(end_m, begin_m + 400.0f);
|
||
} else {
|
||
float fl = env.fog.fog_level > 0 ? float(env.fog.fog_level) : 4.0f;
|
||
begin_m = std::clamp((11.0f - fl) * 45.0f, 60.0f, 500.0f);
|
||
end_m = begin_m + 700.0f;
|
||
}
|
||
e->set_fog_depth_begin(begin_m);
|
||
e->set_fog_depth_end(end_m);
|
||
e->set_fog_depth_curve(0.5f);
|
||
e->set_fog_density(0.0f);
|
||
e->set_fog_sky_affect(0.0f); // 天空自己是渐变,不要被雾再洗一层
|
||
e->set_fog_sun_scatter(0.05f);
|
||
} else {
|
||
e->set_fog_enabled(false);
|
||
}
|
||
|
||
// 色调:参考端是 DX9 定功能、LDR、无 tonemap/HDR。用 LINEAR + 曝光 1 最贴近,
|
||
// Filmic 会抬黑、降饱和 -> 画面发灰。轻微加饱和/对比补回胶片感。
|
||
e->set_tonemapper(godot::Environment::TONE_MAPPER_LINEAR);
|
||
e->set_tonemap_exposure(1.0f);
|
||
e->set_adjustment_enabled(true);
|
||
e->set_adjustment_saturation(1.12f);
|
||
e->set_adjustment_contrast(1.05f);
|
||
e->set_adjustment_brightness(1.0f);
|
||
// glow:参考端没有 bloom。留极轻的,只有真过曝才溢。
|
||
e->set_glow_enabled(true);
|
||
e->set_glow_intensity(0.06f);
|
||
e->set_glow_strength(0.7f);
|
||
e->set_glow_bloom(0.0f);
|
||
e->set_glow_hdr_bleed_threshold(1.6f);
|
||
// SSAO:参考端把接触阴影烘进地形阴影贴图。这里用一点实时 SSAO 代替。
|
||
e->set_ssao_enabled(true);
|
||
e->set_ssao_radius(1.2f);
|
||
e->set_ssao_intensity(0.9f);
|
||
|
||
// Keep parsed reference values visible to the runtime/debug probe even where
|
||
// Godot has no 1:1 mapping (six cube faces, D3D blend factors, cloud motion,
|
||
// and lens flare). This also prevents a future builder from silently losing
|
||
// the fields while the renderer equivalent is being implemented.
|
||
out.world_env->set_meta("msenv_sky_texture_mode", env.sky.texture_render_mode);
|
||
out.world_env->set_meta("msenv_sky_face_count", sky_face_count);
|
||
out.world_env->set_meta("msenv_sky_texture_mode_applied", sky_texture_applied);
|
||
out.world_env->set_meta("msenv_sky_cloud_applied", sky_cloud_applied);
|
||
out.world_env->set_meta("msenv_sky_scale",
|
||
Vector3(env.sky.scale[0], env.sky.scale[1], env.sky.scale[2]));
|
||
out.world_env->set_meta("msenv_cloud_scale",
|
||
Vector2(env.sky.cloud_scale[0], env.sky.cloud_scale[1]));
|
||
out.world_env->set_meta("msenv_cloud_height", env.sky.cloud_height);
|
||
out.world_env->set_meta("msenv_cloud_texture_scale",
|
||
Vector2(env.sky.cloud_texture_scale[0], env.sky.cloud_texture_scale[1]));
|
||
out.world_env->set_meta("msenv_cloud_speed",
|
||
Vector2(env.sky.cloud_speed[0], env.sky.cloud_speed[1]));
|
||
out.world_env->set_meta("msenv_filter_enabled", env.filter.enable);
|
||
out.world_env->set_meta("msenv_filter_color", rgba(env.filter.color));
|
||
out.world_env->set_meta("msenv_filter_alpha_src", env.filter.alpha_src);
|
||
out.world_env->set_meta("msenv_filter_alpha_dest", env.filter.alpha_dest);
|
||
out.world_env->set_meta("msenv_lens_flare_enabled", env.lens_flare.enable);
|
||
out.world_env->set_meta("msenv_lens_flare_texture", env.lens_flare.main_flare_texture.c_str());
|
||
out.world_env->set_meta("msenv_background_light_enabled", env.dir_light.bg_enable);
|
||
out.world_env->set_meta("msenv_character_light_enabled", env.dir_light.ch_enable);
|
||
out.world_env->set_meta("msenv_background_light_ambient", rgba(env.dir_light.bg_ambient));
|
||
out.world_env->set_meta("msenv_character_light_ambient", rgba(env.dir_light.ch_ambient));
|
||
// MapUtil.cpp defaults; Environment_Load has no wind token to override these.
|
||
out.world_env->set_meta("msenv_wind_strength", 0.2f);
|
||
out.world_env->set_meta("msenv_wind_random", 0.0f);
|
||
|
||
out.world_env->set_environment(e);
|
||
return out;
|
||
}
|
||
|
||
} // namespace mtgodot
|