Skip to content

Commit

Permalink
d3d11: use far clip plane and refactor projections
Browse files Browse the repository at this point in the history
RF mainly uses one far clip plane from fog, but there are exceptions:
* skyroom is rendered without far clip plane
* glares and volumetric lights are using doubled far clip plane
Because it is not possible to change projection matrix between objects
(it would break Z-ordering) a different approach was taken. Disable depth
clipping in rasterizer if game wants far clip plane that is farther than
fog clip plane. Frustum culling should deal with glares and volumetric
lights good enough.
Add abstractions for projection to simplify changing it in the future.
Also get rid of zm variable (set it to 1) to simplify the code.
  • Loading branch information
rafalh committed Sep 1, 2024
1 parent 9a2798c commit ca480ed
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 84 deletions.
1 change: 0 additions & 1 deletion game_patch/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ set(SRCS
graphics/d3d11/gr_d3d11_shader.cpp
graphics/d3d11/gr_d3d11_shader.h
graphics/d3d11/gr_d3d11_error.cpp
graphics/d3d11/gr_d3d11_transform.cpp
graphics/d3d11/gr_d3d11_transform.h
graphics/d3d11/gr_d3d11_solid.cpp
graphics/d3d11/gr_d3d11_solid.h
Expand Down
52 changes: 50 additions & 2 deletions game_patch/graphics/d3d11/gr_d3d11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,45 @@ namespace df::gr::d3d11
dyn_geo_renderer_->line_2d(x1, y1, x2, y2, mode);
}

bool Renderer::poly(int nv, rf::gr::Vertex** vertices, int vertex_attributes, rf::gr::Mode mode, bool constant_sw, float sw)
{
rf::ubyte and_code = 0xFF;
for (int i = 0; i < nv; ++i) {
and_code &= vertices[i]->codes;
}
if (and_code) {
return false;
}
for (int i = 0; i < nv; ++i) {
auto v = vertices[i];
if (!(v->flags & rf::gr::VF_PROJECTED)) {
project_vertex(v);
}
if (constant_sw) {
float unscaled_z = sw / matrix_scale.z;
v->sw = render_context_->projection().project_z(unscaled_z);
}
}
tmapper(nv, const_cast<const rf::gr::Vertex**>(vertices), vertex_attributes, mode);
return true;
}

void Renderer::project_vertex(rf::gr::Vertex* v)
{
if (v->flags & rf::gr::VF_PROJECTED) {
return;
}

rf::Vector3 unscaled_world_pos = v->world_pos / matrix_scale;
auto& proj = render_context_->projection();
auto proj_pos = proj.project(unscaled_world_pos);
v->sx = screen.clip_width / 2.0f * (proj_pos.x + 1.0f) + screen.offset_x;
v->sy = screen.clip_height / 2.0f * (1.0f - proj_pos.y) + screen.offset_y;
v->sw = proj_pos.z;

v->flags |= rf::gr::VF_PROJECTED;
}

bool Renderer::set_render_target(int bm_handle)
{
dyn_geo_renderer_->flush();
Expand Down Expand Up @@ -416,9 +455,14 @@ namespace df::gr::d3d11
return texture_manager_->read_back_buffer(back_buffer_, x, y, w, h, data);
}

void Renderer::setup_3d()
void Renderer::setup_3d(Projection proj)
{
render_context_->update_view_proj_transform();
render_context_->update_view_proj_transform(proj);
}

void Renderer::set_far_clip(bool enabled)
{
render_context_->set_depth_clip_enabled(enabled);
}

void Renderer::render_solid(rf::GSolid* solid, rf::GRoom** rooms, int num_rooms)
Expand Down Expand Up @@ -504,4 +548,8 @@ namespace df::gr::d3d11
mesh_renderer_->flush_caches();
}

float Renderer::z_far() const
{
return render_context_->projection().z_far();
}
}
6 changes: 5 additions & 1 deletion game_patch/graphics/d3d11/gr_d3d11.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ namespace df::gr::d3d11
void clear();
void zbuffer_clear();
void set_clip();
void set_far_clip(bool enabled);
void flip();
void texture_save_cache();
void texture_flush_cache(bool force);
Expand All @@ -107,7 +108,9 @@ namespace df::gr::d3d11
void tmapper(int nv, const rf::gr::Vertex **vertices, int vertex_attributes, rf::gr::Mode mode);
void line_3d(const rf::gr::Vertex& v0, const rf::gr::Vertex& v1, rf::gr::Mode mode);
void line_2d(float x1, float y1, float x2, float y2, rf::gr::Mode mode);
void setup_3d();
bool poly(int nv, rf::gr::Vertex** vertices, int vertex_attributes, rf::gr::Mode mode, bool constant_sw, float sw);
void project_vertex(rf::gr::Vertex* v);
void setup_3d(Projection proj);
void render_solid(rf::GSolid* solid, rf::GRoom** rooms, int num_rooms);
void render_movable_solid(rf::GSolid* solid, const rf::Vector3& pos, const rf::Matrix3& orient);
void render_alpha_detail_room(rf::GRoom *room, rf::GSolid *solid);
Expand All @@ -123,6 +126,7 @@ namespace df::gr::d3d11
void page_in_solid(rf::GSolid* solid);
void page_in_movable_solid(rf::GSolid* solid);
void flush_caches();
float z_far() const;

private:
void init_device();
Expand Down
4 changes: 2 additions & 2 deletions game_patch/graphics/d3d11/gr_d3d11_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,11 @@ namespace df::gr::d3d11
DF_GR_D3D11_CHECK_HR(device->CreateBuffer(&desc, nullptr, &buffer_));
}

void ViewProjTransformBuffer::update(ID3D11DeviceContext* device_context)
void ViewProjTransformBuffer::update(const Projection& proj, ID3D11DeviceContext* device_context)
{
ViewProjTransformBufferData data;
data.view_mat = build_view_matrix(rf::gr::eye_pos, rf::gr::eye_matrix);
data.proj_mat = build_proj_matrix();
data.proj_mat = proj.matrix();

D3D11_MAPPED_SUBRESOURCE mapped_subres;
DF_GR_D3D11_CHECK_HR(
Expand Down
28 changes: 23 additions & 5 deletions game_patch/graphics/d3d11/gr_d3d11_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ namespace df::gr::d3d11
public:
ViewProjTransformBuffer(ID3D11Device* device);

void update(ID3D11DeviceContext* device_context);
void update(const Projection& proj, ID3D11DeviceContext* device_context);

operator ID3D11Buffer*() const
{
Expand Down Expand Up @@ -238,9 +238,10 @@ namespace df::gr::d3d11
void zbuffer_clear();
void set_clip();

void update_view_proj_transform()
void update_view_proj_transform(Projection proj)
{
view_proj_transform_cbuffer_.update(device_context_);
projection_ = proj;
view_proj_transform_cbuffer_.update(projection_, device_context_);
}

void update_per_frame_constants()
Expand Down Expand Up @@ -312,10 +313,11 @@ namespace df::gr::d3d11

void set_cull_mode(D3D11_CULL_MODE cull_mode)
{
if (current_cull_mode_ != cull_mode || zbias_changed_) {
if (current_cull_mode_ != cull_mode || zbias_changed_ || depth_clip_enabled_changed_) {
current_cull_mode_ = cull_mode;
zbias_changed_ = false;
set_rasterizer_state(state_manager_.lookup_rasterizer_state(current_cull_mode_, zbias_));
depth_clip_enabled_changed_ = false;
set_rasterizer_state(state_manager_.lookup_rasterizer_state(current_cull_mode_, zbias_, depth_clip_enabled_));
}
}

Expand All @@ -332,6 +334,14 @@ namespace df::gr::d3d11
}
}

void set_depth_clip_enabled(bool depth_clip_enabled)
{
if (depth_clip_enabled_ != depth_clip_enabled) {
depth_clip_enabled_ = depth_clip_enabled;
depth_clip_enabled_changed_ = true;
}
}

void update_lights()
{
lights_buffer_.update(device_context_);
Expand All @@ -342,6 +352,11 @@ namespace df::gr::d3d11
device_context_->DrawIndexed(index_count, index_start_location, base_vertex_location);
}

const Projection& projection() const
{
return projection_;
}

private:
void bind_cbuffers();

Expand Down Expand Up @@ -391,5 +406,8 @@ namespace df::gr::d3d11
ID3D11RasterizerState* current_rasterizer_state_ = nullptr;
int zbias_ = 0;
bool zbias_changed_ = true;
bool depth_clip_enabled_ = true;
bool depth_clip_enabled_changed_ = true;
Projection projection_;
};
}
41 changes: 25 additions & 16 deletions game_patch/graphics/d3d11/gr_d3d11_dynamic_geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
#include "gr_d3d11_shader.h"
#include "gr_d3d11_context.h"
#include "../../rf/os/frametime.h"
#define NO_D3D8
#include "../../rf/gr/gr_direct3d.h"

using namespace rf;

Expand Down Expand Up @@ -77,6 +75,23 @@ namespace df::gr::d3d11
return color;
}

inline std::array<float, 4> DynamicGeometryRenderer::convert_pos(const gr::Vertex& v, bool is_3d)
{
Vector3 ndc{
((v.sx - gr::screen.offset_x) / gr::screen.clip_width * 2.0f - 1.0f),
((v.sy - gr::screen.offset_y) / gr::screen.clip_height * -2.0f + 1.0f),
v.sw,
};
// Set w to depth in camera space (needed for 3D rendering)
float w = is_3d ? render_context_.projection().unproject_z(v.sw) : 1.0f;
return {
ndc.x * w,
ndc.y * w,
ndc.z * w,
w,
};
}

void DynamicGeometryRenderer::add_poly(int nv, const gr::Vertex **vertices, int vertex_attributes, const std::array<int, 2>& tex_handles, gr::Mode mode)
{
int num_index = (nv - 2) * 3;
Expand Down Expand Up @@ -106,16 +121,13 @@ namespace df::gr::d3d11
if (use_vert_alpha && !(vertex_attributes & gr::TMAP_FLAG_ALPHA)) {
color.alpha = gr::screen.current_color.alpha;
}
// Note: gr_matrix_scale is zero before first gr_setup_3d call
float matrix_scale_z = gr::matrix_scale.z ? gr::matrix_scale.z : 1.0f;
for (int i = 0; i < nv; ++i) {
const gr::Vertex& in_vert = *vertices[i];
GpuTransformedVertex& out_vert = gpu_verts[i];
// Set w to depth in camera space (needed for 3D rendering)
float w = 1.0f / in_vert.sw / matrix_scale_z;
out_vert.x = ((in_vert.sx - gr::screen.offset_x) / gr::screen.clip_width * 2.0f - 1.0f) * w;
out_vert.y = ((in_vert.sy - gr::screen.offset_y) / gr::screen.clip_height * -2.0f + 1.0f) * w;
out_vert.z = in_vert.sw * gr::d3d::zm * w;
auto [x, y, z, w] = convert_pos(in_vert, true);
out_vert.x = x;
out_vert.y = y;
out_vert.z = z;
out_vert.w = w;

if (use_vert_color && (vertex_attributes & gr::TMAP_FLAG_RGB)) {
Expand Down Expand Up @@ -154,16 +166,13 @@ namespace df::gr::d3d11
rf::Color color = get_vertex_color_from_screen(mode);
int diffuse = pack_color(color);

// Note: gr_matrix_scale is zero before first gr_setup_3d call
float matrix_scale_z = gr::matrix_scale.z ? gr::matrix_scale.z : 1.0f;
for (int i = 0; i < num_verts; ++i) {
const gr::Vertex& in_vert = *vertices[i];
GpuTransformedVertex& out_vert = gpu_verts[i];
out_vert.x = (in_vert.sx - gr::screen.offset_x) / gr::screen.clip_width * 2.0f - 1.0f;
out_vert.y = (in_vert.sy - gr::screen.offset_y) / gr::screen.clip_height * -2.0f + 1.0f;
float w = is_3d ? 1.0f / in_vert.sw / matrix_scale_z : 1.0f;
out_vert.z = in_vert.sw * gr::d3d::zm * w;
// Set w to depth in camera space (needed for 3D rendering)
auto [x, y, z, w] = convert_pos(in_vert, is_3d);
out_vert.x = x;
out_vert.y = y;
out_vert.z = z;
out_vert.w = w;
out_vert.diffuse = diffuse;
}
Expand Down
2 changes: 2 additions & 0 deletions game_patch/graphics/d3d11/gr_d3d11_dynamic_geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ namespace df::gr::d3d11
return {gpu_verts, gpu_inds, base_vertex};
}

std::array<float, 4> convert_pos(const rf::gr::Vertex& v, bool is_3d);

ComPtr<ID3D11Device> device_;
RenderContext& render_context_;
RingBuffer<GpuTransformedVertex> vertex_ring_buffer_;
Expand Down
38 changes: 35 additions & 3 deletions game_patch/graphics/d3d11/gr_d3d11_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,17 @@ namespace df::gr::d3d11
renderer->set_fullscreen_state(rf::gr::screen.window_mode == rf::gr::FULLSCREEN);
}

rf::ubyte project_vertex_new(Vertex* v)
{
renderer->project_vertex(v);
return v->flags;
}

bool poly(int nv, rf::gr::Vertex** vertices, int vertex_attributes, rf::gr::Mode mode, bool constant_sw, float sw)
{
return renderer->poly(nv, vertices, vertex_attributes, mode, constant_sw, sw);
}

static CodeInjection g_render_room_objects_render_liquid_injection{
0x004D4106,
[](auto& regs) {
Expand All @@ -211,7 +222,27 @@ namespace df::gr::d3d11
static CodeInjection gr_d3d_setup_3d_injection{
0x005473E4,
[]() {
renderer->setup_3d();
float sx = matrix_scale.x / matrix_scale.z;
float sy = matrix_scale.y / matrix_scale.z;
static auto& zm = addr_as_ref<float>(0x005A7DD8);
float zn = 0.1f; // static near plane (RF uses: zm / matrix_scale.z)
zm = 1.0f; // let's not use zm at all to simplify software projections
float zf = rf::level.distance_fog_far_clip > 0.0f ? rf::level.distance_fog_far_clip : 1700.0f;
renderer->setup_3d(Projection{sx, sy, zn, zf});
},
};

static CodeInjection gr_d3d_setup_fustrum_injection{
0x00546A40,
[]() {
// Glares and volumetric lights use doubled far clip plane
// To avoid using "user clip planes" disable depth clipping
// Frustum culling should be enough
static auto& use_far_clip = addr_as_ref<bool>(0x01818B65);
static auto& far_clip_dist = addr_as_ref<float>(0x01818B68);
float z_far = renderer->z_far();
bool depth_clip_enable = use_far_clip && far_clip_dist < z_far * 1.1f;
renderer->set_far_clip(depth_clip_enable);
},
};

Expand Down Expand Up @@ -281,6 +312,7 @@ void gr_d3d11_apply_patch()

g_render_room_objects_render_liquid_injection.install();
gr_d3d_setup_3d_injection.install();
gr_d3d_setup_fustrum_injection.install();
vif_lod_mesh_ctor_injection.install();
vif_lod_mesh_dtor_injection.install();
v3d_page_in_injection.install();
Expand All @@ -304,7 +336,7 @@ void gr_d3d11_apply_patch()
//AsmWriter{0x00547150}.ret(); // gr_d3d_setup_3d
//AsmWriter{0x005473F0}.ret(); // gr_d3d_start_instance
//AsmWriter{0x00547540}.ret(); // gr_d3d_stop_instance
//AsmWriter{0x005477A0}.ret(); // gr_d3d_project_vertex
AsmWriter{0x005477A0}.jmp(project_vertex_new); // gr_d3d_project_vertex
//AsmWriter{0x005478F0}.ret(); // gr_d3d_is_normal_facing
//AsmWriter{0x00547960}.ret(); // gr_d3d_is_normal_facing_plane
//AsmWriter{0x005479B0}.ret(); // gr_d3d_get_apparent_distance_from_camera
Expand Down Expand Up @@ -344,7 +376,7 @@ void gr_d3d11_apply_patch()
//AsmWriter{0x00558450}.ret(); // gr_d3d_render_glass_shard - uses gr_poly
AsmWriter{0x00558550}.ret(); // gr_d3d_render_face_wireframe
//AsmWriter{0x005585F0}.ret(); // gr_d3d_render_weapon_tracer - uses gr_poly
//AsmWriter{0x005587C0}.ret(); // gr_d3d_poly - uses gr_d3d_tmapper
AsmWriter{0x005587C0}.jmp(poly); // gr_d3d_poly
AsmWriter{0x00558920}.ret(); // gr_d3d_render_geometry_wireframe
AsmWriter{0x00558960}.ret(); // gr_d3d_render_geometry_in_editor
AsmWriter{0x00558C40}.ret(); // gr_d3d_render_sel_face_in_editor
Expand Down
3 changes: 2 additions & 1 deletion game_patch/graphics/d3d11/gr_d3d11_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ namespace df::gr::d3d11
{
}

ComPtr<ID3D11RasterizerState> StateManager::create_rasterizer_state(D3D11_CULL_MODE cull_mode, int depth_bias)
ComPtr<ID3D11RasterizerState> StateManager::create_rasterizer_state(D3D11_CULL_MODE cull_mode, int depth_bias, bool depth_clip_enable)
{
CD3D11_RASTERIZER_DESC desc{CD3D11_DEFAULT{}};
desc.CullMode = cull_mode;
desc.DepthBias = depth_bias;
if (g_game_config.msaa) {
desc.MultisampleEnable = TRUE;
}
desc.DepthClipEnable = depth_clip_enable;

ComPtr<ID3D11RasterizerState> rasterizer_state;
check_hr(
Expand Down
Loading

0 comments on commit ca480ed

Please sign in to comment.