native render: transliterate D3D8 fixed-function pipeline (unit A)
- Texture coordinates: TEXCOORDINDEX generation (camera-space normal / position / reflection vector, LOCALVIEWER) and D3DTS_TEXTUREn under TEXTURETRANSFORMFLAGS (incl. PROJECTED) now run in native.vert; the RecordingDevice CPU uv baking and the sphere-map / multiplicative-shadow shader hacks are removed. Godot consumer uses Render3DStageTexcoords. - Lighting: 8 D3D lights in camera space, 1/(a0+a1d+a2d^2) without clamp, spot cone theta/2 phi/2 with falloff, diffuse/ambient/emissive material sources, NORMALIZENORMALS, inverse-transpose normal matrix. - Alpha test compares 8-bit alpha against ALPHAREF with ALPHAFUNC. - Back-buffer Clear() is recorded in draw order and executed with vkCmdClearAttachments; render pass clears to 0xff000000. - D3DVIEWPORT8 (incl. MinZ/MaxZ) is dynamic per-draw state. - Missing vertex diffuse is opaque white; alpha blend mirrors SRC/DESTBLEND. - CScreen::ms_clearDepth / ms_diffuseColor initialised to 40250's 1.0f / 0xffffffff (were zero; exposed once clears were executed). - Capture format v8; --screenshot-out FILE.bmp swapchain readback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
425e6f37fb
commit
5285ca85fd
@@ -424,12 +424,12 @@ auto CScreen::Identity() -> void
|
||||
STATEMANAGER.SetTransform(D3DTS_WORLD, &ms_matIdentity);
|
||||
}
|
||||
|
||||
decltype(CScreen::ms_diffuseColor) CScreen::ms_diffuseColor{};
|
||||
decltype(CScreen::ms_diffuseColor) CScreen::ms_diffuseColor = 0xffffffff;
|
||||
|
||||
decltype(CScreen::ms_clearColor) CScreen::ms_clearColor{};
|
||||
|
||||
decltype(CScreen::ms_clearStencil) CScreen::ms_clearStencil{};
|
||||
|
||||
decltype(CScreen::ms_clearDepth) CScreen::ms_clearDepth{};
|
||||
decltype(CScreen::ms_clearDepth) CScreen::ms_clearDepth = 1.0f;
|
||||
|
||||
decltype(CScreen::ms_frustum) CScreen::ms_frustum{};
|
||||
|
||||
@@ -414,7 +414,16 @@ public:
|
||||
HRESULT Clear(DWORD, const D3DRECT*, DWORD flags, D3DCOLOR color, float depth, DWORD) override
|
||||
{
|
||||
if (m_renderTarget == m_backBuffer)
|
||||
{
|
||||
// Kept in draw order: the renderer clears its colour/depth attachments at this point.
|
||||
Render3DDraw clear;
|
||||
clear.clear_flags = flags & (D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL);
|
||||
clear.clear_color = color;
|
||||
clear.clear_z = depth;
|
||||
if (clear.clear_flags)
|
||||
Render3DAdd(std::move(clear));
|
||||
return S_OK;
|
||||
}
|
||||
auto* surface = static_cast<CpuSurface*>(m_renderTarget);
|
||||
if (!surface->parent || surface->level != 0 || surface->desc.Format != D3DFMT_R5G6B5)
|
||||
return S_OK;
|
||||
@@ -781,6 +790,8 @@ private:
|
||||
draw.viewport[1] = float(m_viewport.Y);
|
||||
draw.viewport[2] = float(m_viewport.Width);
|
||||
draw.viewport[3] = float(m_viewport.Height);
|
||||
draw.viewport_z[0] = m_viewport.MinZ;
|
||||
draw.viewport_z[1] = m_viewport.MaxZ;
|
||||
draw.pretransformed = layout.rhw;
|
||||
draw.lines = type == D3DPT_LINELIST || type == D3DPT_LINESTRIP;
|
||||
|
||||
@@ -794,15 +805,6 @@ private:
|
||||
if ((size_t(hi) + 1) * stride > vertexBytes)
|
||||
return;
|
||||
const size_t count = size_t(hi - lo) + 1;
|
||||
const bool texgen_cam_pos0 = layout.uv0 < 0 && m_textures[0] != nullptr &&
|
||||
(m_stageStates[0][D3DTSS_TEXCOORDINDEX] & 0xFFFF0000u) == D3DTSS_TCI_CAMERASPACEPOSITION &&
|
||||
m_stageStates[0][D3DTSS_TEXTURETRANSFORMFLAGS] == D3DTTFF_COUNT2;
|
||||
const bool texgen_cam_pos1 = layout.uv1 < 0 && m_textures[1] != nullptr &&
|
||||
(m_stageStates[1][D3DTSS_TEXCOORDINDEX] & 0xFFFF0000u) == D3DTSS_TCI_CAMERASPACEPOSITION &&
|
||||
m_stageStates[1][D3DTSS_TEXTURETRANSFORMFLAGS] == D3DTTFF_COUNT2;
|
||||
const bool tex_xform0 = layout.uv0 >= 0 && m_textures[0] != nullptr &&
|
||||
(m_stageStates[0][D3DTSS_TEXCOORDINDEX] & 0xFFFF0000u) == 0 &&
|
||||
m_stageStates[0][D3DTSS_TEXTURETRANSFORMFLAGS] == D3DTTFF_COUNT2;
|
||||
GpuSkinSubrangeView skin_view;
|
||||
const bool gpu_skinned = source_vertex != nullptr &&
|
||||
LookupGpuSkinSubrange(vertices, stride, lo, hi, &skin_view);
|
||||
@@ -839,40 +841,9 @@ private:
|
||||
key = mix(key, type);
|
||||
key = mix(key, stride);
|
||||
key = mix(key, m_fvf);
|
||||
if (texgen_cam_pos0)
|
||||
key = mix(key, static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(m_textures[0])));
|
||||
if (texgen_cam_pos1)
|
||||
key = mix(key, static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(m_textures[1])));
|
||||
draw.geometry_key = (key & 0x7FFFFFFFFFFFFFFFull) | 1ull;
|
||||
std::uint64_t revision = mix(14695981039346656037ull, source_vertex->revision);
|
||||
revision = mix(revision, source_index ? source_index->revision : 0);
|
||||
if (tex_xform0)
|
||||
{
|
||||
const float* m = m_transforms[D3DTS_TEXTURE0];
|
||||
for (int mi : { 0, 1, 4, 5, 8, 9, 12, 13 })
|
||||
{
|
||||
std::uint32_t bits = 0;
|
||||
std::memcpy(&bits, &m[mi], sizeof(bits));
|
||||
revision = mix(revision, bits);
|
||||
}
|
||||
}
|
||||
// Camera-space texture coordinates are baked into the captured geometry.
|
||||
// The terrain splat and character-shadow matrices can change while the
|
||||
// vertex/index buffers remain unchanged, so they are part of its revision.
|
||||
for (int stage = 0; stage < 2; ++stage)
|
||||
{
|
||||
if ((stage == 0 && !texgen_cam_pos0) || (stage == 1 && !texgen_cam_pos1))
|
||||
continue;
|
||||
float worldView[16], worldTexture[16];
|
||||
multiply(m_transforms[256], m_transforms[D3DTS_VIEW], worldView);
|
||||
multiply(worldView, m_transforms[stage == 0 ? D3DTS_TEXTURE0 : D3DTS_TEXTURE1], worldTexture);
|
||||
for (int mi : { 0, 1, 4, 5, 8, 9, 12, 13 })
|
||||
{
|
||||
std::uint32_t bits = 0;
|
||||
std::memcpy(&bits, &worldTexture[mi], sizeof(bits));
|
||||
revision = mix(revision, bits);
|
||||
}
|
||||
}
|
||||
draw.geometry_revision = revision & 0x7FFFFFFFFFFFFFFFull;
|
||||
}
|
||||
}
|
||||
@@ -912,49 +883,6 @@ private:
|
||||
if (layout.uv1 >= 0) std::memcpy(&draw.uv1[i * 2], v + layout.uv1, 8);
|
||||
if (layout.diffuse >= 0) std::memcpy(&draw.diffuse[i], v + layout.diffuse, 4);
|
||||
}
|
||||
if (texgen_cam_pos0)
|
||||
{
|
||||
float worldView[16], worldTex0[16];
|
||||
multiply(m_transforms[256], m_transforms[D3DTS_VIEW], worldView);
|
||||
multiply(worldView, m_transforms[D3DTS_TEXTURE0], worldTex0);
|
||||
draw.uv0.resize(count * 2);
|
||||
const bool clamp0_u = m_stageStates[0][D3DTSS_ADDRESSU] == D3DTADDRESS_CLAMP;
|
||||
const bool clamp0_v = m_stageStates[0][D3DTSS_ADDRESSV] == D3DTADDRESS_CLAMP;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
const float pos[4] = { draw.positions[i * 3 + 0], draw.positions[i * 3 + 1], draw.positions[i * 3 + 2], 1.0f };
|
||||
float tc[4];
|
||||
transform_point(pos, worldTex0, tc);
|
||||
draw.uv0[i * 2 + 0] = clamp0_u ? std::clamp(tc[0], 0.5f / 256.0f, 255.5f / 256.0f) : tc[0];
|
||||
draw.uv0[i * 2 + 1] = clamp0_v ? std::clamp(tc[1], 0.5f / 256.0f, 255.5f / 256.0f) : tc[1];
|
||||
}
|
||||
}
|
||||
else if (tex_xform0)
|
||||
{
|
||||
const float* m = m_transforms[D3DTS_TEXTURE0];
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
const float u = draw.uv0[i * 2 + 0];
|
||||
const float v = draw.uv0[i * 2 + 1];
|
||||
draw.uv0[i * 2 + 0] = u * m[0] + v * m[4] + m[8] + m[12];
|
||||
draw.uv0[i * 2 + 1] = u * m[1] + v * m[5] + m[9] + m[13];
|
||||
}
|
||||
}
|
||||
if (texgen_cam_pos1)
|
||||
{
|
||||
float worldView[16], worldTex1[16];
|
||||
multiply(m_transforms[256], m_transforms[D3DTS_VIEW], worldView);
|
||||
multiply(worldView, m_transforms[D3DTS_TEXTURE1], worldTex1);
|
||||
draw.uv1.resize(count * 2);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
const float pos[4] = { draw.positions[i * 3 + 0], draw.positions[i * 3 + 1], draw.positions[i * 3 + 2], 1.0f };
|
||||
float tc[4];
|
||||
transform_point(pos, worldTex1, tc);
|
||||
draw.uv1[i * 2 + 0] = tc[0];
|
||||
draw.uv1[i * 2 + 1] = tc[1];
|
||||
}
|
||||
}
|
||||
if (gpu_skinned)
|
||||
{
|
||||
const std::size_t rel_lo = std::size_t(lo - skin_view.mesh_base_vertex);
|
||||
@@ -1033,7 +961,10 @@ private:
|
||||
draw.ambient = m_renderStates[D3DRS_AMBIENT];
|
||||
draw.diffuse_material_source = m_renderStates[D3DRS_DIFFUSEMATERIALSOURCE];
|
||||
draw.ambient_material_source = m_renderStates[D3DRS_AMBIENTMATERIALSOURCE];
|
||||
draw.emissive_material_source = m_renderStates[D3DRS_EMISSIVEMATERIALSOURCE];
|
||||
draw.color_vertex = m_renderStates[D3DRS_COLORVERTEX];
|
||||
draw.normalize_normals = m_renderStates[D3DRS_NORMALIZENORMALS];
|
||||
draw.local_viewer = m_renderStates[D3DRS_LOCALVIEWER];
|
||||
for (int stage = 0; stage < 2; ++stage)
|
||||
{
|
||||
draw.color_op[stage] = m_stageStates[stage][D3DTSS_COLOROP];
|
||||
@@ -1047,6 +978,10 @@ private:
|
||||
draw.min_filter[stage] = m_stageStates[stage][D3DTSS_MINFILTER];
|
||||
draw.mag_filter[stage] = m_stageStates[stage][D3DTSS_MAGFILTER];
|
||||
draw.mip_filter[stage] = m_stageStates[stage][D3DTSS_MIPFILTER];
|
||||
draw.border_color[stage] = m_stageStates[stage][D3DTSS_BORDERCOLOR];
|
||||
draw.texcoord_index[stage] = m_stageStates[stage][D3DTSS_TEXCOORDINDEX];
|
||||
draw.texture_transform_flags[stage] = m_stageStates[stage][D3DTSS_TEXTURETRANSFORMFLAGS];
|
||||
std::memcpy(draw.texture_matrix[stage], m_transforms[D3DTS_TEXTURE0 + stage], sizeof(draw.texture_matrix[stage]));
|
||||
}
|
||||
copy_color(draw.material_diffuse, m_material.Diffuse);
|
||||
copy_color(draw.material_ambient, m_material.Ambient);
|
||||
@@ -1060,7 +995,7 @@ private:
|
||||
copy_color(draw.light0_diffuse, m_lights[0].Diffuse);
|
||||
copy_color(draw.light0_ambient, m_lights[0].Ambient);
|
||||
}
|
||||
for (int i = 0; i < 2; ++i)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
if (!m_lightEnabled[i]) continue;
|
||||
const D3DLIGHT8& source = m_lights[i];
|
||||
@@ -1217,3 +1152,100 @@ void Render3DAdd(Render3DDraw draw)
|
||||
}
|
||||
|
||||
const std::vector<Render3DDraw>& Render3DDraws() { return g_draws; }
|
||||
|
||||
// D3D8 fixed-function texture coordinate processing for one stage (the native renderer's
|
||||
// native.vert applies the same rules on the GPU):
|
||||
// - D3DTSS_TEXCOORDINDEX low word picks the vertex set, a 2D set enters the transform as (u, v, 1, 0)
|
||||
// (so a D3DTS_TEXTUREn translation lives in _31/_32, as CSkyBox::RenderCloud writes it);
|
||||
// - D3DTSS_TCI_CAMERASPACENORMAL/POSITION/REFLECTIONVECTOR generate (x, y, z, 1) in camera space;
|
||||
// - D3DTSS_TEXTURETRANSFORMFLAGS != DISABLE multiplies by D3DTS_TEXTUREn (row vector), and
|
||||
// D3DTTFF_PROJECTED divides by the last counted element.
|
||||
void Render3DStageTexcoords(const Render3DDraw& draw, int stage, std::vector<float>& out)
|
||||
{
|
||||
const size_t count = draw.positions.size() / 3;
|
||||
out.assign(count * 2, 0.0f);
|
||||
if (stage < 0 || stage > 1)
|
||||
return;
|
||||
const std::uint32_t tci = draw.texcoord_index[stage];
|
||||
const std::uint32_t gen = tci & 0xFFFF0000u;
|
||||
const std::uint32_t set = tci & 0xFFFFu;
|
||||
const std::vector<float>* uv = set == 0 ? &draw.uv0 : (set == 1 ? &draw.uv1 : nullptr);
|
||||
const std::uint32_t flags = draw.texture_transform_flags[stage];
|
||||
const std::uint32_t elements = flags & 0xFFu;
|
||||
const bool projected = (flags & D3DTTFF_PROJECTED) != 0;
|
||||
float worldView[16] = {};
|
||||
for (int row = 0; row < 4; ++row)
|
||||
for (int col = 0; col < 4; ++col)
|
||||
for (int k = 0; k < 4; ++k)
|
||||
worldView[row * 4 + col] += draw.world[row * 4 + k] * draw.view[k * 4 + col];
|
||||
// Normals go through the inverse transpose of world * view (cofactors / determinant).
|
||||
const float* w = worldView;
|
||||
float normalMatrix[9] = {
|
||||
w[5] * w[10] - w[6] * w[9], -(w[4] * w[10] - w[6] * w[8]), w[4] * w[9] - w[5] * w[8],
|
||||
-(w[1] * w[10] - w[2] * w[9]), w[0] * w[10] - w[2] * w[8], -(w[0] * w[9] - w[1] * w[8]),
|
||||
w[1] * w[6] - w[2] * w[5], -(w[0] * w[6] - w[2] * w[4]), w[0] * w[5] - w[1] * w[4] };
|
||||
const float det = w[0] * normalMatrix[0] + w[1] * normalMatrix[1] + w[2] * normalMatrix[2];
|
||||
if (det != 0.0f)
|
||||
for (float& c : normalMatrix) c /= det;
|
||||
const float* m = draw.texture_matrix[stage];
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
float in[4] = { 0, 0, 1, 0 };
|
||||
if (gen == 0 || draw.pretransformed)
|
||||
{
|
||||
if (uv && uv->size() >= (i + 1) * 2)
|
||||
{
|
||||
in[0] = (*uv)[i * 2 + 0];
|
||||
in[1] = (*uv)[i * 2 + 1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const float* p = &draw.positions[i * 3];
|
||||
float eye[3], n[3] = { 0, 0, 0 };
|
||||
for (int c = 0; c < 3; ++c)
|
||||
eye[c] = p[0] * worldView[c] + p[1] * worldView[4 + c] + p[2] * worldView[8 + c] + worldView[12 + c];
|
||||
if (draw.normals.size() >= (i + 1) * 3)
|
||||
{
|
||||
const float* src = &draw.normals[i * 3];
|
||||
for (int c = 0; c < 3; ++c)
|
||||
n[c] = src[0] * normalMatrix[c] + src[1] * normalMatrix[3 + c] + src[2] * normalMatrix[6 + c];
|
||||
if (draw.normalize_normals)
|
||||
{
|
||||
const float len = std::sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
|
||||
if (len > 0.0f)
|
||||
for (float& c : n) c /= len;
|
||||
}
|
||||
}
|
||||
float v[3] = { 0, 0, 0 };
|
||||
if (gen == D3DTSS_TCI_CAMERASPACENORMAL)
|
||||
std::memcpy(v, n, sizeof(v));
|
||||
else if (gen == D3DTSS_TCI_CAMERASPACEPOSITION)
|
||||
std::memcpy(v, eye, sizeof(v));
|
||||
else if (gen == D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR)
|
||||
{
|
||||
float e[3] = { 0, 0, -1 };
|
||||
if (draw.local_viewer)
|
||||
{
|
||||
const float len = std::sqrt(eye[0] * eye[0] + eye[1] * eye[1] + eye[2] * eye[2]);
|
||||
for (int c = 0; c < 3; ++c) e[c] = len > 0.0f ? -eye[c] / len : 0.0f;
|
||||
}
|
||||
const float en = e[0] * n[0] + e[1] * n[1] + e[2] * n[2];
|
||||
for (int c = 0; c < 3; ++c) v[c] = 2.0f * en * n[c] - e[c];
|
||||
}
|
||||
in[0] = v[0]; in[1] = v[1]; in[2] = v[2]; in[3] = 1.0f;
|
||||
}
|
||||
float r[4] = { in[0], in[1], in[2], in[3] };
|
||||
// Pre-transformed (XYZRHW) vertices pass their coordinates through untransformed.
|
||||
if (elements != D3DTTFF_DISABLE && !draw.pretransformed)
|
||||
for (int c = 0; c < 4; ++c)
|
||||
r[c] = in[0] * m[c] + in[1] * m[4 + c] + in[2] * m[8 + c] + in[3] * m[12 + c];
|
||||
if (projected && !draw.pretransformed && elements >= 2 && elements <= 4 && r[elements - 1] != 0.0f)
|
||||
{
|
||||
r[0] /= r[elements - 1];
|
||||
r[1] /= r[elements - 1];
|
||||
}
|
||||
out[i * 2 + 0] = r[0];
|
||||
out[i * 2 + 1] = r[1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,13 @@ struct Render3DDraw {
|
||||
float view[16];
|
||||
float proj[16];
|
||||
float viewport[4] = {}; // x, y, width, height
|
||||
float viewport_z[2] = {0, 1}; // MinZ, MaxZ
|
||||
|
||||
// IDirect3DDevice8::Clear on the back buffer, kept in draw order. A clear entry has no geometry;
|
||||
// clear_flags holds D3DCLEAR_TARGET / D3DCLEAR_ZBUFFER / D3DCLEAR_STENCIL.
|
||||
std::uint32_t clear_flags = 0;
|
||||
std::uint32_t clear_color = 0; // 0xAARRGGBB
|
||||
float clear_z = 1;
|
||||
|
||||
// Stage 0/1 textures, named like UIRenderTextureName: the pack path of a file texture or
|
||||
// "mem:<id>@<revision>"; empty when the stage has no texture.
|
||||
@@ -51,6 +58,14 @@ struct Render3DDraw {
|
||||
std::uint32_t alpha_op[2] = {}, alpha_arg1[2] = {}, alpha_arg2[2] = {};
|
||||
std::uint32_t address_u[2] = {}, address_v[2] = {};
|
||||
std::uint32_t min_filter[2] = {}, mag_filter[2] = {}, mip_filter[2] = {};
|
||||
std::uint32_t border_color[2] = {};
|
||||
// D3DTSS_TEXCOORDINDEX (set index | D3DTSS_TCI_* generation mode), D3DTSS_TEXTURETRANSFORMFLAGS and
|
||||
// D3DTS_TEXTURE0/1 per stage. The renderer generates and transforms texture coordinates from
|
||||
// these the way the D3D8 fixed-function vertex pipeline does; uv0/uv1 stay the raw vertex sets.
|
||||
std::uint32_t texcoord_index[2] = {0, 1};
|
||||
std::uint32_t texture_transform_flags[2] = {};
|
||||
float texture_matrix[2][16] = {{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1},
|
||||
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}};
|
||||
// D3DMATERIAL8 diffuse/ambient/emissive (r, g, b, a) and light 0 when enabled.
|
||||
float material_diffuse[4] = {1, 1, 1, 1};
|
||||
float material_ambient[4] = {};
|
||||
@@ -69,12 +84,20 @@ struct Render3DDraw {
|
||||
float attenuation[3] = {};
|
||||
float range = 0;
|
||||
float theta = 0, phi = 0, falloff = 0;
|
||||
} lights[2];
|
||||
} lights[8];
|
||||
std::uint32_t diffuse_material_source = 0;
|
||||
std::uint32_t ambient_material_source = 0;
|
||||
std::uint32_t emissive_material_source = 0;
|
||||
std::uint32_t color_vertex = 0;
|
||||
std::uint32_t normalize_normals = 0; // D3DRS_NORMALIZENORMALS
|
||||
std::uint32_t local_viewer = 1; // D3DRS_LOCALVIEWER
|
||||
};
|
||||
|
||||
// The texture coordinates stage 0/1 samples at each vertex, computed on the CPU with the same
|
||||
// D3D8 fixed-function rules the native renderer's vertex shader applies (TEXCOORDINDEX generation,
|
||||
// then D3DTS_TEXTUREn under TEXTURETRANSFORMFLAGS). For consumers without that shader (Godot).
|
||||
void Render3DStageTexcoords(const Render3DDraw& draw, int stage, std::vector<float>& out);
|
||||
|
||||
struct GpuSkinSubrangeView {
|
||||
std::uint64_t source_mesh_key = 0;
|
||||
std::uint32_t mesh_base_vertex = 0;
|
||||
|
||||
@@ -403,6 +403,8 @@ Array Metin2PythonHost::render3d_draws() {
|
||||
out.resize(static_cast<int64_t>(draws.size()));
|
||||
int64_t draw_index = 0;
|
||||
for (const Render3DDraw &draw : draws) {
|
||||
if (draw.clear_flags)
|
||||
continue; // back-buffer clears are consumed by the native renderer only
|
||||
Dictionary item;
|
||||
item["geometry_key"] = static_cast<int64_t>(draw.geometry_key);
|
||||
item["geometry_revision"] = static_cast<int64_t>(draw.geometry_revision);
|
||||
@@ -414,8 +416,14 @@ Array Metin2PythonHost::render3d_draws() {
|
||||
item["pretransformed"] = draw.pretransformed;
|
||||
item["lines"] = draw.lines;
|
||||
|
||||
// Stage-0 coordinates the D3D8 pipeline generates or transforms depend on per-draw matrices,
|
||||
// so such draws carry them outside the geometry cache.
|
||||
const bool stage0_texgen = (draw.texcoord_index[0] & 0xFFFF0000u) != 0 ||
|
||||
(draw.texcoord_index[0] & 0xFFFFu) != 0 || draw.texture_transform_flags[0] != 0;
|
||||
if (stage0_texgen)
|
||||
item["geometry_key"] = static_cast<int64_t>(0);
|
||||
Dictionary geometry;
|
||||
if (draw.geometry_key != 0) {
|
||||
if (draw.geometry_key != 0 && !stage0_texgen) {
|
||||
auto cached = geometry_cache.find(draw.geometry_key);
|
||||
if (cached != geometry_cache.end() && cached->second.revision == draw.geometry_revision) {
|
||||
cached->second.last_used = extraction;
|
||||
@@ -461,7 +469,11 @@ Array Metin2PythonHost::render3d_draws() {
|
||||
}
|
||||
return out;
|
||||
};
|
||||
if (!draw.uv0.empty())
|
||||
if (stage0_texgen) {
|
||||
std::vector<float> stage_uv;
|
||||
Render3DStageTexcoords(draw, 0, stage_uv);
|
||||
geometry["uv0"] = uvs(stage_uv);
|
||||
} else if (!draw.uv0.empty())
|
||||
geometry["uv0"] = uvs(draw.uv0);
|
||||
if (!draw.uv1.empty())
|
||||
geometry["uv1"] = uvs(draw.uv1);
|
||||
@@ -477,7 +489,7 @@ Array Metin2PythonHost::render3d_draws() {
|
||||
for (int64_t i = 0; i < indices.size(); ++i)
|
||||
indices[i] = static_cast<int32_t>(draw.indices[i]);
|
||||
geometry["indices"] = indices;
|
||||
if (draw.geometry_key != 0)
|
||||
if (draw.geometry_key != 0 && !stage0_texgen)
|
||||
geometry_cache[draw.geometry_key] = {draw.geometry_revision, extraction, geometry};
|
||||
}
|
||||
item.merge(geometry);
|
||||
@@ -530,6 +542,7 @@ Array Metin2PythonHost::render3d_draws() {
|
||||
item["viewport"] = vp;
|
||||
out[draw_index++] = item;
|
||||
}
|
||||
out.resize(draw_index);
|
||||
if (extraction % 120 == 0) {
|
||||
for (auto it = geometry_cache.begin(); it != geometry_cache.end();) {
|
||||
if (extraction - it->second.last_used > 120)
|
||||
|
||||
Reference in New Issue
Block a user