Files
metin2-client/src/host/render_state.cpp
T
shenleiandClaude Opus 5.5 75d2dd8545 host: split src/host/main.cpp into translation units
main.cpp (4.5k lines) -> main.cpp (entry, args, synthetic benchmark) plus
vulkan_window, live_client, frame_policy, native_audio, texture_decode,
render_state, input_keymap, host_util. Bodies are moved verbatim; multi-line
VulkanWindow / NativeAudioEngine / FrameRatePolicy members are defined out of
line; one-line accessors stay inline. Anonymous namespace -> mt_host.

No behavior change: ctest 15/15, port_gate macos+android PASS, desktop
--live-client login screen and synthetic --draws runs unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:30:44 +09:00

131 lines
6.7 KiB
C++

#include "render_state.h"
namespace mt_host {
std::array<float, 16> multiply(const float* a, const float* b) {
std::array<float, 16> result{};
for (int row = 0; row < 4; ++row)
for (int col = 0; col < 4; ++col)
for (int k = 0; k < 4; ++k)
result[row * 4 + col] += a[row * 4 + k] * b[k * 4 + col];
return result;
}
std::array<float, 16> draw_mvp(const Render3DDraw& draw) {
const auto world_view = multiply(draw.world, draw.view);
return multiply(world_view.data(), draw.proj);
}
std::array<float, 4> unpack_argb(std::uint32_t argb) {
return {
float((argb >> 16) & 255) / 255.0f,
float((argb >> 8) & 255) / 255.0f,
float(argb & 255) / 255.0f,
float((argb >> 24) & 255) / 255.0f};
}
// Inverse transpose of the upper 3x3 of a row-vector matrix, the D3D8 normal transform.
std::array<float, 16> normal_matrix(const std::array<float, 16>& m) {
const float a = m[0], b = m[1], c = m[2], d = m[4], e = m[5], f = m[6], g = m[8], h = m[9], i = m[10];
const float A = e * i - f * h, B = -(d * i - f * g), C = d * h - e * g;
const float D = -(b * i - c * h), E = a * i - c * g, F = -(a * h - b * g);
const float G = b * f - c * e, H = -(a * f - c * d), I = a * e - b * d;
const float det = a * A + b * B + c * C;
std::array<float, 16> out = kIdentityMatrix;
if (det == 0.0f) return out;
const float s = 1.0f / det;
// inverse = adjugate / det; its transpose is the cofactor matrix / det.
out[0] = A * s; out[1] = B * s; out[2] = C * s;
out[4] = D * s; out[5] = E * s; out[6] = F * s;
out[8] = G * s; out[9] = H * s; out[10] = I * s;
return out;
}
PushConstants make_push_constants(const Render3DDraw& draw, float skin_offset_encoded) {
PushConstants constants{};
constants.mvp = draw_mvp(draw);
constants.params = {skin_offset_encoded, draw.pretransformed ? 1.0f : 0.0f, 0.0f, 0.0f};
return constants;
}
FixedFunctionState make_fixed_function_state(const Render3DDraw& draw) {
FixedFunctionState state{};
for (int stage = 0; stage < 2; ++stage) {
state.stage_color[stage] = {draw.color_op[stage], draw.color_arg1[stage], draw.color_arg2[stage],
draw.texcoord_index[stage]};
state.stage_alpha[stage] = {draw.alpha_op[stage], draw.alpha_arg1[stage], draw.alpha_arg2[stage],
draw.texture_transform_flags[stage]};
std::copy(draw.texture_matrix[stage], draw.texture_matrix[stage] + 16, state.texture_matrix[stage].begin());
}
state.texture_factor = unpack_argb(draw.texture_factor);
state.world_view = multiply(draw.world, draw.view);
state.normal_matrix = normal_matrix(state.world_view);
state.fog_color = unpack_argb(draw.fog_color);
state.fog_params = {draw.fog_start, draw.fog_end, draw.fog_density,
draw.fog_range_enable ? 1.0f : 0.0f};
const std::uint32_t fog_mode = draw.fog_enable
? (draw.fog_table_mode ? draw.fog_table_mode : (draw.pretransformed ? 4u : draw.fog_vertex_mode)) : 0;
state.flags = {1u, !draw.texture0.empty() ? 1u : 0u, !draw.texture1.empty() ? 1u : 0u, fog_mode};
state.lighting_flags = {draw.lighting, draw.color_vertex, draw.diffuse.empty() ? 0u : 1u,
draw.normalize_normals};
state.material_sources = {draw.diffuse_material_source, draw.ambient_material_source,
draw.emissive_material_source, draw.local_viewer};
state.alpha_test = {draw.alpha_test, draw.alpha_func, draw.alpha_ref, 0};
std::copy(draw.material_diffuse, draw.material_diffuse + 4, state.material_diffuse.begin());
std::copy(draw.material_ambient, draw.material_ambient + 4, state.material_ambient.begin());
std::copy(draw.material_emissive, draw.material_emissive + 4, state.material_emissive.begin());
state.global_ambient = unpack_argb(draw.ambient);
const float* v = draw.view;
for (int i = 0; i < 8; ++i) {
const auto& light = draw.lights[i];
const float* p = light.position;
const float* d = light.direction;
std::array<float, 3> position{}, direction{};
for (int c = 0; c < 3; ++c) {
position[c] = p[0] * v[c] + p[1] * v[4 + c] + p[2] * v[8 + c] + v[12 + c];
direction[c] = d[0] * v[c] + d[1] * v[4 + c] + d[2] * v[8 + c];
}
const float length = std::sqrt(direction[0] * direction[0] + direction[1] * direction[1] +
direction[2] * direction[2]);
if (length > 0.0f)
for (float& c : direction) c /= length;
state.light_position_type[i] = {position[0], position[1], position[2], float(light.type)};
state.light_direction_range[i] = {direction[0], direction[1], direction[2], light.range};
std::copy(light.diffuse, light.diffuse + 4, state.light_diffuse[i].begin());
std::copy(light.ambient, light.ambient + 4, state.light_ambient[i].begin());
state.light_attenuation[i] = {light.attenuation[0], light.attenuation[1], light.attenuation[2], light.falloff};
state.light_spot[i] = {std::cos(light.theta * 0.5f), std::cos(light.phi * 0.5f), 0, 0};
}
return state;
}
std::uint64_t hash_bytes(std::uint64_t hash, const void* bytes, std::size_t size) {
const auto* data = static_cast<const std::uint8_t*>(bytes);
std::size_t i = 0;
for (; i + sizeof(std::uint64_t) <= size; i += sizeof(std::uint64_t)) {
std::uint64_t word = 0;
std::memcpy(&word, data + i, sizeof(word));
hash = (hash ^ word) * 1099511628211ull;
}
for (; i < size; ++i) hash = (hash ^ data[i]) * 1099511628211ull;
hash = (hash ^ size) * 1099511628211ull;
return hash;
}
// Immediate-mode draws have no source-buffer key. For them, verify content before reusing a slot.
std::uint64_t geometry_hash(const Render3DDraw& draw) {
std::uint64_t hash = 14695981039346656037ull;
const std::uint32_t flags = (draw.lines ? 1u : 0u) | (draw.pretransformed ? 2u : 0u);
hash = hash_bytes(hash, &flags, sizeof(flags));
hash = hash_bytes(hash, draw.positions.data(), draw.positions.size() * sizeof(float));
hash = hash_bytes(hash, draw.rhw.data(), draw.rhw.size() * sizeof(float));
hash = hash_bytes(hash, draw.vertex_fog.data(), draw.vertex_fog.size() * sizeof(float));
hash = hash_bytes(hash, draw.normals.data(), draw.normals.size() * sizeof(float));
hash = hash_bytes(hash, draw.uv0.data(), draw.uv0.size() * sizeof(float));
hash = hash_bytes(hash, draw.uv1.data(), draw.uv1.size() * sizeof(float));
hash = hash_bytes(hash, draw.diffuse.data(), draw.diffuse.size() * sizeof(std::uint32_t));
return hash_bytes(hash, draw.indices.data(), draw.indices.size() * sizeof(std::uint32_t));
}
} // namespace mt_host