render parity unit G: 40250 tiling selection + verbatim STP terrain
- PythonApplication::Create mirrors 40250 :1196-1251 (SOFTWARE_TILING cfg, auto tiling IsFastTNL ? HTP : STP, MinLOD on !IsFastTNL) - RecordingDevice caps: VertexShaderVersion 1.1 + CLIPTLVERTS (fast T&L -> HTP) - MapOutdoorRenderSTP.cpp replaced by the full 40250 source (native gate only) - native_render XYZRHW: fix inverted screen->NDC Y, negative rhw clip, no texture transform on TL vertices, specular-alpha vertex fog (capture v9) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
91ce13c3cf
commit
863f3771af
@@ -23,11 +23,12 @@
|
||||
// - Version 8: eight lights, back-buffer clear entries, viewport MinZ/MaxZ, border color,
|
||||
// TEXCOORDINDEX / TEXTURETRANSFORMFLAGS / D3DTS_TEXTUREn per stage, emissive
|
||||
// material source, NORMALIZENORMALS and LOCALVIEWER; UVs are raw vertex sets.
|
||||
// - Version 9: adds XYZRHW vertex fog (specular alpha).
|
||||
// Capture is opt-in and never runs in the normal game path.
|
||||
namespace native_draw_capture {
|
||||
|
||||
struct Capture {
|
||||
std::uint32_t version = 8;
|
||||
std::uint32_t version = 9;
|
||||
std::vector<Render3DDraw> draws;
|
||||
std::unordered_map<std::string, std::vector<std::uint8_t>> textures;
|
||||
std::uint32_t ui_width = 960;
|
||||
@@ -113,7 +114,7 @@ inline void write(
|
||||
std::ofstream file(path, std::ios::binary | std::ios::trunc);
|
||||
if (!file) throw std::runtime_error("cannot create native draw capture: " + path);
|
||||
const std::uint32_t magic = 0x4d544452; // MTDR
|
||||
const std::uint32_t version = 8;
|
||||
const std::uint32_t version = 9;
|
||||
const auto count = static_cast<std::uint32_t>(draws.size());
|
||||
write_scalar(file, magic); write_scalar(file, version); write_scalar(file, count);
|
||||
for (const auto& draw : draws) {
|
||||
@@ -188,6 +189,7 @@ inline void write(
|
||||
write_scalar(file, draw.emissive_material_source);
|
||||
write_scalar(file, draw.normalize_normals);
|
||||
write_scalar(file, draw.local_viewer);
|
||||
write_vector(file, draw.vertex_fog);
|
||||
write_vector(file, draw.bone_indices);
|
||||
write_vector(file, draw.bone_weights);
|
||||
write_vector(file, draw.bone_matrices);
|
||||
@@ -237,7 +239,7 @@ inline Capture read_capture(const std::string& path) {
|
||||
if (!file) throw std::runtime_error("cannot open native draw capture: " + path);
|
||||
std::uint32_t magic = 0, version = 0, count = 0;
|
||||
read_scalar(file, magic); read_scalar(file, version); read_scalar(file, count);
|
||||
if (magic != 0x4d544452 || (version < 1 || version > 8) || count > 10'000)
|
||||
if (magic != 0x4d544452 || (version < 1 || version > 9) || count > 10'000)
|
||||
throw std::runtime_error("unsupported native draw capture format");
|
||||
Capture capture;
|
||||
capture.version = version;
|
||||
@@ -328,6 +330,7 @@ inline Capture read_capture(const std::string& path) {
|
||||
read_scalar(file, draw.emissive_material_source);
|
||||
read_scalar(file, draw.normalize_normals);
|
||||
read_scalar(file, draw.local_viewer);
|
||||
if (version >= 9) read_vector(file, draw.vertex_fog);
|
||||
} else if (draw.light0) {
|
||||
auto& light = draw.lights[0];
|
||||
light.type = 3;
|
||||
|
||||
+11
-6
@@ -59,6 +59,7 @@ struct Vertex {
|
||||
float weights[4];
|
||||
float mask_uv[2];
|
||||
float rhw = 1.0f;
|
||||
float vertex_fog = 1.0f;
|
||||
};
|
||||
|
||||
struct PushConstants {
|
||||
@@ -166,7 +167,7 @@ FixedFunctionState make_fixed_function_state(const Render3DDraw& draw) {
|
||||
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.fog_vertex_mode) : 0;
|
||||
? (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};
|
||||
@@ -221,6 +222,7 @@ std::uint64_t geometry_hash(const Render3DDraw& draw) {
|
||||
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));
|
||||
@@ -1422,10 +1424,11 @@ public:
|
||||
if (draw.pretransformed) {
|
||||
const float screen_width = float(logical_width());
|
||||
const float screen_height = float(logical_height());
|
||||
// Screen pixels (y down) to D3D NDC (y up); the shader's Y flip then maps to Vulkan.
|
||||
constants.mvp = {2.0f / screen_width, 0, 0, 0,
|
||||
0, 2.0f / screen_height, 0, 0,
|
||||
0, -2.0f / screen_height, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
-1, -1, 0, 1};
|
||||
-1, 1, 0, 1};
|
||||
}
|
||||
PreparedDraw prepared_draw{};
|
||||
prepared_draw.geometry = &geometry;
|
||||
@@ -2354,7 +2357,7 @@ private:
|
||||
stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fragment_module_; stages[1].pName = "main";
|
||||
|
||||
VkVertexInputBindingDescription binding{0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX};
|
||||
VkVertexInputAttributeDescription attributes[8] = {
|
||||
VkVertexInputAttributeDescription attributes[9] = {
|
||||
{0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, position)},
|
||||
{1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)},
|
||||
{2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)},
|
||||
@@ -2362,10 +2365,11 @@ private:
|
||||
{4, 0, VK_FORMAT_R8G8B8A8_UINT, offsetof(Vertex, joints)},
|
||||
{5, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, weights)},
|
||||
{6, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, mask_uv)},
|
||||
{7, 0, VK_FORMAT_R32_SFLOAT, offsetof(Vertex, rhw)}};
|
||||
{7, 0, VK_FORMAT_R32_SFLOAT, offsetof(Vertex, rhw)},
|
||||
{8, 0, VK_FORMAT_R32_SFLOAT, offsetof(Vertex, vertex_fog)}};
|
||||
VkPipelineVertexInputStateCreateInfo input{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
|
||||
input.vertexBindingDescriptionCount = 1; input.pVertexBindingDescriptions = &binding;
|
||||
input.vertexAttributeDescriptionCount = 8; input.pVertexAttributeDescriptions = attributes;
|
||||
input.vertexAttributeDescriptionCount = 9; input.pVertexAttributeDescriptions = attributes;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo assembly{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
||||
assembly.topology = lines ? VK_PRIMITIVE_TOPOLOGY_LINE_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
@@ -2867,6 +2871,7 @@ private:
|
||||
vertices[i].position[1] = draw.positions[3*i+1];
|
||||
vertices[i].position[2] = draw.positions[3*i+2];
|
||||
vertices[i].rhw = i < draw.rhw.size() ? draw.rhw[i] : 1.0f;
|
||||
vertices[i].vertex_fog = i < draw.vertex_fog.size() ? draw.vertex_fog[i] : 1.0f;
|
||||
if (3*i + 2 < draw.normals.size()) {
|
||||
vertices[i].normal[0] = draw.normals[3*i];
|
||||
vertices[i].normal[1] = draw.normals[3*i+1];
|
||||
|
||||
@@ -7,6 +7,7 @@ layout(location = 4) in uvec4 in_joints;
|
||||
layout(location = 5) in vec4 in_weights;
|
||||
layout(location = 6) in vec2 in_mask_uv;
|
||||
layout(location = 7) in float in_rhw;
|
||||
layout(location = 8) in float in_vertex_fog;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec2 out_uv;
|
||||
@@ -73,7 +74,9 @@ vec2 stage_texcoord(int stage, bool pretransformed, vec3 eye, vec3 n) {
|
||||
? (length(eye) > 0.0 ? -normalize(eye) : vec3(0.0)) : vec3(0.0, 0.0, -1.0);
|
||||
coord = vec4(2.0 * dot(e, n) * n - e, 1.0);
|
||||
}
|
||||
uint transform_flags = fixed_state.stage_alpha[stage].w;
|
||||
// Pretransformed (XYZRHW) vertices bypass D3D8 T&L, texture transforms included
|
||||
// (RecordingDevice's CPU texcoord path does the same).
|
||||
uint transform_flags = pretransformed ? 0u : fixed_state.stage_alpha[stage].w;
|
||||
uint elements = transform_flags & 0xFFu;
|
||||
vec4 r = coord;
|
||||
if (elements != 0u)
|
||||
@@ -109,7 +112,9 @@ void main() {
|
||||
// D3D row-vector matrices are uploaded row-major. GLSL reads the bytes as their transpose.
|
||||
gl_Position = draw.mvp * vec4(pos, 1.0);
|
||||
if (pretransformed) {
|
||||
float clip_w = in_rhw > 0.0 ? 1.0 / in_rhw : 1.0;
|
||||
// Vertices behind the eye carry a negative rhw (STP terrain): rebuilding clip space
|
||||
// with w = 1/rhw < 0 lets the clipper drop them instead of drawing w = 1 spikes.
|
||||
float clip_w = in_rhw != 0.0 ? 1.0 / in_rhw : 1.0;
|
||||
gl_Position.xyz *= clip_w;
|
||||
gl_Position.w = clip_w;
|
||||
}
|
||||
@@ -172,8 +177,13 @@ void main() {
|
||||
out_mask_uv = stage_texcoord(1, pretransformed, eye, n);
|
||||
|
||||
out_fog = 1.0;
|
||||
if (fixed_state.flags.w != 0u && !pretransformed) {
|
||||
float distance_to_eye = fixed_state.fog_params.w > 0.5 ? length(eye) : abs(eye.z);
|
||||
if (fixed_state.flags.w == 4u) {
|
||||
// XYZRHW with FOGTABLEMODE NONE: D3D8 takes the vertex fog from the specular alpha.
|
||||
out_fog = in_vertex_fog;
|
||||
} else if (fixed_state.flags.w != 0u) {
|
||||
// Table fog on XYZRHW vertices runs on eye depth w = 1/rhw.
|
||||
float distance_to_eye = pretransformed ? gl_Position.w
|
||||
: (fixed_state.fog_params.w > 0.5 ? length(eye) : abs(eye.z));
|
||||
if (fixed_state.flags.w == 3u) {
|
||||
float span = fixed_state.fog_params.y - fixed_state.fog_params.x;
|
||||
if (span > 0.0001)
|
||||
|
||||
Reference in New Issue
Block a user