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>
131 lines
6.7 KiB
C++
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
|