single sided tris working

This commit is contained in:
water111
2025-06-16 22:28:35 -04:00
parent a06348fa9f
commit 753d8257be
13 changed files with 536 additions and 158 deletions
+7 -6
View File
@@ -590,15 +590,17 @@ void MercModelGroup::serialize(Serializer& ser) {
void ShadowModel::serialize(Serializer& ser) {
ser.from_str(&name);
ser.from_ptr(&max_bones);
ser.from_ptr(&single_tris);
ser.from_ptr(&double_tris);
ser.from_ptr(&single_edges);
ser.from_ptr(&double_edges);
ser.from_ptr(&first_vertex);
ser.from_ptr(&num_one_bone_vertices);
ser.from_ptr(&num_two_bone_vertices);
ser.from_pod_vector(&single_tris);
ser.from_pod_vector(&double_tris);
ser.from_pod_vector(&single_edges);
ser.from_pod_vector(&double_edges);
}
void ShadowModelGroup::serialize(Serializer& ser) {
ser.from_pod_vector(&vertices);
ser.from_pod_vector(&indices);
if (ser.is_saving()) {
ser.save<size_t>(models.size());
} else {
@@ -794,7 +796,6 @@ void Hfragment::memory_usage(tfrag3::MemoryUsageTracker* tracker) const {
void ShadowModelGroup::memory_usage(MemoryUsageTracker* tracker) const {
tracker->add(SHADOW_VERTS, vertices.size() * sizeof(ShadowVertex));
tracker->add(SHADOW_INDEX, indices.size() * sizeof(u32));
}
void Level::memory_usage(MemoryUsageTracker* tracker) const {
+18 -6
View File
@@ -623,23 +623,35 @@ struct ShadowVertex {
u8 mats[2];
u8 flags;
};
static_assert(sizeof(ShadowVertex) == 20);
struct ShadowTri {
u8 verts[3];
};
struct ShadowEdge {
u8 ind[2];
u8 tri[2];
};
struct ShadowModel {
static constexpr int kMaxVertices = 254;
static constexpr int kMaxTris = 254;
std::string name;
u32 max_bones;
struct Run {
u32 first_index;
u32 count;
};
Run single_tris, double_tris, single_edges, double_edges;
std::vector<ShadowTri> single_tris, double_tris;
std::vector<ShadowEdge> single_edges, double_edges;
u32 first_vertex;
u32 num_one_bone_vertices;
u32 num_two_bone_vertices;
void serialize(Serializer& ser);
};
struct ShadowModelGroup {
std::vector<ShadowVertex> vertices;
std::vector<u32> indices;
std::vector<ShadowModel> models;
void serialize(Serializer& ser);
void memory_usage(MemoryUsageTracker* tracker) const;
+50 -48
View File
@@ -276,6 +276,41 @@ std::vector<tfrag3::ShadowVertex> convert_vertices(const ShadowData& data) {
return result;
}
tfrag3::ShadowTri convert_tri(const ShadowTri& tri) {
tfrag3::ShadowTri result;
for (int i = 0; i < 3; i++) {
result.verts[i] = tri.verts[i];
}
return result;
}
tfrag3::ShadowEdge convert_edge(const ShadowEdge& edge) {
tfrag3::ShadowEdge result;
for (int i = 0; i < 2; i++) {
result.ind[i] = edge.ind[i];
result.tri[i] = edge.tri[i];
}
return result;
}
std::vector<tfrag3::ShadowTri> convert_tris(const std::vector<ShadowTri>& tris) {
std::vector<tfrag3::ShadowTri> result;
result.reserve(tris.size());
for (auto& tri : tris) {
result.push_back(convert_tri(tri));
}
return result;
}
std::vector<tfrag3::ShadowEdge> convert_edges(const std::vector<ShadowEdge>& edges) {
std::vector<tfrag3::ShadowEdge> result;
result.reserve(edges.size());
for (auto& edge : edges) {
result.push_back(convert_edge(edge));
}
return result;
}
void extract_shadow(const ObjectFileData& ag_data,
const DecompilerTypeSystem& dts,
tfrag3::Level& out,
@@ -297,8 +332,22 @@ void extract_shadow(const ObjectFileData& ag_data,
for (auto loc : geo_locations) {
const ShadowData data = extract_shadow_data(ag_data.linked_data, dts, loc);
auto& model = sd.models.emplace_back();
model.name = data.name;
model.max_bones = data.num_joints;
model.single_tris = convert_tris(data.single_tris);
model.double_tris = convert_tris(data.double_tris);
model.single_edges = convert_edges(data.single_edges);
model.double_edges = convert_edges(data.double_edges);
const u32 vertex_offset = sd.vertices.size();
const u32 num_vertices = data.one_bone_vertices.size() + data.two_bone_vertices.size();
model.first_vertex = vertex_offset;
model.num_one_bone_vertices = data.one_bone_vertices.size();
model.num_two_bone_vertices = data.two_bone_vertices.size();
ASSERT(model.num_one_bone_vertices + model.num_two_bone_vertices <=
tfrag3::ShadowModel::kMaxVertices);
ASSERT(model.single_tris.size() + model.double_tris.size() <= tfrag3::ShadowModel::kMaxTris);
// insert top vertices
auto vertices = convert_vertices(data);
@@ -310,53 +359,6 @@ void extract_shadow(const ObjectFileData& ag_data,
}
sd.vertices.insert(sd.vertices.end(), vertices.begin(), vertices.end());
auto& model = sd.models.emplace_back();
model.name = data.name;
model.max_bones = data.num_joints;
// single triangles
model.single_tris.first_index = sd.indices.size();
for (auto& stri : data.single_tris) {
sd.indices.push_back(static_cast<u32>(stri.verts[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(stri.verts[1]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(stri.verts[2]) + vertex_offset);
}
model.single_tris.count = sd.indices.size() - model.single_tris.first_index;
// double triangles
model.double_tris.first_index = sd.indices.size();
for (auto& dtri : data.double_tris) {
sd.indices.push_back(static_cast<u32>(dtri.verts[0]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(dtri.verts[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(dtri.verts[2]) + vertex_offset + num_vertices);
}
model.double_tris.count = sd.indices.size() - model.double_tris.first_index;
// single edges
model.single_edges.first_index = sd.indices.size();
for (auto& se : data.single_edges) {
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset);
}
model.single_edges.count = sd.indices.size() - model.single_edges.first_index;
model.double_edges.first_index = sd.indices.size();
for (auto& se : data.double_edges) {
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset + num_vertices);
sd.indices.push_back(static_cast<u32>(se.ind[0]) + vertex_offset);
sd.indices.push_back(static_cast<u32>(se.ind[1]) + vertex_offset);
}
model.double_edges.count = sd.indices.size() - model.double_edges.first_index;
if (dump_level) {
auto file_path = file_util::get_file_path(
{"debug_out/shadow", fmt::format("{}_{}.ply", ag_data.name_in_dgo, i)});
+1
View File
@@ -56,6 +56,7 @@ set(RUNTIME_SOURCE
graphics/opengl_renderer/foreground/Merc2BucketRenderer.cpp
graphics/opengl_renderer/foreground/Shadow2.cpp
graphics/opengl_renderer/foreground/Shadow3.cpp
graphics/opengl_renderer/foreground/Shadow3CPU.cpp
graphics/opengl_renderer/loader/Loader.cpp
graphics/opengl_renderer/loader/LoaderStages.cpp
graphics/opengl_renderer/ocean/CommonOceanRenderer.cpp
@@ -6,6 +6,9 @@ Shadow3::Shadow3(ShaderLibrary& shaders) {
glGenVertexArrays(1, &m_opengl.vao);
glBindVertexArray(m_opengl.vao);
glGenBuffers(1, &m_opengl.indices);
glGenBuffers(1, &m_opengl.debug_verts);
glGenBuffers(1, &m_opengl.bones_buffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_opengl.bones_buffer);
@@ -43,13 +46,14 @@ Shadow3::Shadow3(ShaderLibrary& shaders) {
Shadow3::~Shadow3() {
glDeleteBuffers(1, &m_opengl.bones_buffer);
glDeleteBuffers(1, &m_opengl.indices);
glDeleteBuffers(1, &m_opengl.debug_verts);
glDeleteVertexArrays(1, &m_opengl.vao);
}
void Shadow3::setup_for_level(SharedRenderState* render_state, const LevelData* level_data) {
glBindVertexArray(m_opengl.vao);
glBindBuffer(GL_ARRAY_BUFFER, level_data->shadow_vertices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, level_data->shadow_indices);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
@@ -95,11 +99,25 @@ void set_uniform(GLuint uniform, const math::Vector3f& val) {
void set_uniform(GLuint uniform, const math::Vector4f& val) {
glUniform4f(uniform, val.x(), val.y(), val.z(), val.w());
}
} // namespace
void Shadow3::draw_model(SharedRenderState* render_state,
ShadowRequest* request,
ScopedProfilerNode& prof) {
ShadowCPUInput input{
.origin = request->origin,
.top_plane = request->top_plane,
.bottom_plane = request->bottom_plane,
.light_dir = request->light_dir,
.bones = request->bones,
.model = request->model.model,
.vertices = &request->model.level->level->shadow_data.vertices,
.flags = request->flags,
.debug_highlight_tri = m_debug_tri,
};
calc_shadow_indices(input, &m_cpu_workspace, &m_cpu_output);
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_opengl.bones_buffer,
sizeof(math::Vector4f) * request->bone_idx, 128 * 16 * 4);
const auto* geo = request->model.model;
@@ -108,53 +126,118 @@ void Shadow3::draw_model(SharedRenderState* render_state,
set_uniform(m_uniforms.top_plane, request->top_plane);
set_uniform(m_uniforms.bottom_plane, request->bottom_plane);
// enable stencil!
glEnable(GL_STENCIL_TEST);
glStencilMask(0xFF);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glDepthFunc(GL_GEQUAL);
// glDepthMask(GL_FALSE); // no depth writes.
// glDrawElements(GL_TRIANGLES, m_cpu_output.num_indices, GL_UNSIGNED_INT, nullptr);
if (m_hacks) {
auto* model = request->model.model;
int num_verts = model->num_one_bone_vertices + model->num_two_bone_vertices;
std::vector<tfrag3::ShadowVertex> verts;
for (size_t i = 0; i < num_verts; ++i) {
auto& out = verts.emplace_back();
out.flags = 255;
out.mats[0] = 255;
out.mats[1] = 255;
out.pos[0] = m_cpu_workspace.vertices[i].x();
out.pos[1] = m_cpu_workspace.vertices[i].y();
out.pos[2] = m_cpu_workspace.vertices[i].z();
out.weight = m_cpu_workspace.vertices[i].w();
}
auto do_draw = [&](const tfrag3::ShadowModel::Run& run, const math::Vector3f& color) {
set_uniform(m_uniforms.debug_color, color);
glDrawElements(GL_TRIANGLES, run.count, GL_UNSIGNED_INT,
(void*)(sizeof(u32) * run.first_index));
for (size_t i = 0; i < num_verts; ++i) {
auto& out = verts.emplace_back();
out.flags = 255;
out.mats[0] = 255;
out.mats[1] = 255;
out.pos[0] = m_cpu_workspace.dual_vertices[i].x();
out.pos[1] = m_cpu_workspace.dual_vertices[i].y();
out.pos[2] = m_cpu_workspace.dual_vertices[i].z();
out.weight = m_cpu_workspace.dual_vertices[i].w();
}
for (int i = 0; i < m_cpu_output.num_f0_indices; i++) {
m_cpu_output.f0_indices[i] -= model->first_vertex;
}
for (int i = 0; i < m_cpu_output.num_f1_indices; i++) {
m_cpu_output.f1_indices[i] -= model->first_vertex;
}
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
set_uniform(m_uniforms.debug_color,color);
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// glDrawElements(GL_TRIANGLES, run.count, GL_UNSIGNED_INT,
// (void*)(sizeof(u32) * run.first_index));
// glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
};
glDepthFunc(GL_GEQUAL);
glDepthMask(GL_TRUE);
// glEnable(GL_CULL_FACE);
// glCullFace(GL_BACK);
glBindBuffer(GL_ARRAY_BUFFER, m_opengl.debug_verts);
glBufferData(GL_ARRAY_BUFFER, num_verts * 2 * sizeof(tfrag3::ShadowVertex), verts.data(),
GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_opengl.indices);
set_uniform(m_uniforms.debug_color, math::Vector3f(0.5f, 0.5f, 0.5f));
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_cpu_output.num_f0_indices * sizeof(u32),
m_cpu_output.f0_indices, GL_DYNAMIC_DRAW);
glDrawElements(GL_TRIANGLES, m_cpu_output.num_f0_indices, GL_UNSIGNED_INT, nullptr);
set_uniform(m_uniforms.debug_color, math::Vector3f(0.f, 0.f, 0.f));
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, m_cpu_output.num_f0_indices, GL_UNSIGNED_INT, nullptr);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
auto do_all_draws = [&]( const math::Vector3f& color) {
glUniform1i(m_uniforms.bottom_cap, 0);
do_draw(geo->single_tris, color);
// do_draw(geo->double_tris, math::Vector3f(0.8, 0.5, 0.5));
do_draw(geo->single_edges, color);
// do_draw(geo->double_edges, math::Vector3f(0.5, 0.8, 0.5));
// glUniform1i(m_uniforms.bottom_cap, 1);
// do_draw(geo->single_tris, math::Vector3f(0.5, 0.5, 0.8));
// do_draw(geo->double_tris, math::Vector3f(0.5, 0.5, 0.8));
};
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_cpu_output.num_f1_indices * sizeof(u32),
m_cpu_output.f1_indices, GL_DYNAMIC_DRAW);
set_uniform(m_uniforms.debug_color, math::Vector3f(0.f, 0.f, 0.f));
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, m_cpu_output.num_f1_indices, GL_UNSIGNED_INT, nullptr);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// using glCullFace(GL_FRONT) seems to give us back faces.
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
set_uniform(m_uniforms.debug_color, math::Vector3f(0.5f, 0.78f, 0.5f));
glDrawElements(GL_TRIANGLES, m_cpu_output.num_f1_indices, GL_UNSIGNED_INT, nullptr);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glStencilFunc(GL_ALWAYS, 0, 0); // always pass stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment on depth pass.
do_all_draws({0.1f, 0.1f, 0.8f});
glCullFace(GL_FRONT);
glStencilFunc(GL_ALWAYS, 0, 0);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); // decrement on depth pass.
do_all_draws({0.8f, 0.1f, 0.1f});
glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, request->model.level->shadow_vertices);
glDisable(GL_CULL_FACE);
} else {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_opengl.indices);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_cpu_output.num_indices * sizeof(u32),
m_cpu_output.indices, GL_DYNAMIC_DRAW);
// enable stencil!
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no color writes.
glEnable(GL_STENCIL_TEST);
glStencilMask(0xFF);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glDepthFunc(GL_GEQUAL);
glDepthMask(GL_FALSE); // no depth writes.
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
glStencilFunc(GL_ALWAYS, 0, 0); // always pass stencil
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); // increment on depth fail
glDrawElements(GL_TRIANGLES, m_cpu_output.num_indices, GL_UNSIGNED_INT, nullptr);
glCullFace(GL_BACK);
glStencilFunc(GL_ALWAYS, 0, 0);
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); // decrement on depth pass.
glDrawElements(GL_TRIANGLES, m_cpu_output.num_indices, GL_UNSIGNED_INT, nullptr);
glDisable(GL_CULL_FACE);
}
}
void Shadow3::finish(SharedRenderState* render_state, ScopedProfilerNode& prof) {
// finally, draw shadow.
if (!m_hacks) {
glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glDepthFunc(GL_ALWAYS);
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ZERO);
glColorMask(true, true, true, false);
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
m_full_screen_draw.draw(math::Vector4f(0.5, 0.4, 0.3, 0.5), render_state, prof);
}
// restore
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glBlendEquation(GL_FUNC_ADD);
glDepthMask(GL_TRUE);
glDisable(GL_STENCIL_TEST);
}
void Shadow3::flush_requests(SharedRenderState* render_state, ScopedProfilerNode& prof) {
@@ -202,7 +285,11 @@ void Shadow3::first_time_setup(SharedRenderState* render_state) {
set_uniform(m_uniforms.hvdf_offset, render_state->camera_hvdf_off);
}
void Shadow3::draw_debug_window() {}
void Shadow3::draw_debug_window() {
ImGui::Checkbox("hacks", &m_hacks);
ImGui::Checkbox("near_plane", &m_near_plane_hack);
ImGui::InputInt("Tri", &m_debug_tri);
}
void Shadow3::render_jak1(DmaFollower& dma,
SharedRenderState* render_state,
@@ -283,6 +370,8 @@ void Shadow3::render_jak1(DmaFollower& dma,
// (somewhere in the model) and following the shadow direction backward.
request.origin = game_request.settings.center +
game_request.settings.shadow_dir * game_request.settings.dist_to_locus;
request.bones = g_ee_main_mem + game_request.mtx;
request.flags = game_request.settings.flags;
// copy bones to buffer
constexpr int in_stride = 8 * 4 * sizeof(float);
@@ -299,11 +388,14 @@ void Shadow3::render_jak1(DmaFollower& dma,
request.top_plane = game_request.settings.top_plane;
request.bottom_plane = game_request.settings.bot_plane;
if (!(kAbsolutePlanes & game_request.settings.flags)) {
printf("relative plane mode, base is %f, move by %f\n",
-request.bottom_plane.w() / 4096.0, game_request.settings.center.y() / 4096.0);
// printf("relative plane mode, base is %f, move by %f\n",
// -request.bottom_plane.w() / 4096.0, game_request.settings.center.y() / 4096.0);
// in relative planes mode, the height of the plane is adjusted to be relative to the
// height of the center, so the planes move and down with the model
request.top_plane.w() -= game_request.settings.center.y();
if (m_near_plane_hack) {
request.bottom_plane.w() = 4096;
}
request.bottom_plane.w() -= game_request.settings.center.y();
}
@@ -312,7 +404,7 @@ void Shadow3::render_jak1(DmaFollower& dma,
if (render_state->camera_pos.xyz().dot(request.bottom_plane.xyz()) +
request.bottom_plane.w() <
0) {
printf(" SKIP: camera below lower clipping plane.\n");
// printf(" SKIP: camera below lower clipping plane.\n");
m_next_request--;
continue;
}
@@ -321,15 +413,15 @@ void Shadow3::render_jak1(DmaFollower& dma,
// detect if the origin is below the clipping plane and if so, move it up.
const float dot = request.bottom_plane.xyz().dot(request.origin);
if (dot + request.bottom_plane.w() > 0) {
printf(" the origin is below the clipping plane, moving it up.\n");
printf(" center was %s\n", game_request.settings.center.to_string_aligned().c_str());
printf(" dir was %s\n", game_request.settings.shadow_dir.to_string_aligned().c_str());
printf(" locus %f\n", game_request.settings.dist_to_locus);
printf(" bottom plane was %s\n",
game_request.settings.bot_plane.to_string_aligned().c_str());
printf(" adjusted bottom plane was %s\n",
request.bottom_plane.to_string_aligned().c_str());
printf(" abs flag %d\n", game_request.settings.flags & kAbsolutePlanes);
// printf(" the origin is below the clipping plane, moving it up.\n");
// printf(" center was %s\n", game_request.settings.center.to_string_aligned().c_str());
// printf(" dir was %s\n", game_request.settings.shadow_dir.to_string_aligned().c_str());
// printf(" locus %f\n", game_request.settings.dist_to_locus);
// printf(" bottom plane was %s\n",
// game_request.settings.bot_plane.to_string_aligned().c_str());
// printf(" adjusted bottom plane was %s\n",
// request.bottom_plane.to_string_aligned().c_str());
// printf(" abs flag %d\n", game_request.settings.flags & kAbsolutePlanes);
request.bottom_plane.w() = -dot;
}
@@ -353,20 +445,20 @@ void Shadow3::render_jak1(DmaFollower& dma,
return math::Vector4f(xyz.x(), xyz.y(), xyz.z(), in.w() - xyz.dot(cam_rot[3].xyz()));
};
printf("plane offset before: %f\n",
game_request.settings.center.dot(request.bottom_plane.xyz()) +
request.bottom_plane.w());
// printf("plane offset before: %f\n",
// game_request.settings.center.dot(request.bottom_plane.xyz()) +
// request.bottom_plane.w());
request.light_dir = rotate(request.light_dir);
request.top_plane = rotate_plane(request.top_plane);
request.bottom_plane = rotate_plane(request.bottom_plane);
request.origin = transform(request.origin);
printf("plane offset after: %f\n",
transform(game_request.settings.center).dot(request.bottom_plane.xyz()) +
request.bottom_plane.w());
printf("rot3: %s\n", cam_rot[3].to_string_aligned().c_str());
printf(" 2: %s\n", cam_pos.to_string_aligned().c_str());
// printf("plane offset after: %f\n",
// transform(game_request.settings.center).dot(request.bottom_plane.xyz()) +
// request.bottom_plane.w());
// printf("rot3: %s\n", cam_rot[3].to_string_aligned().c_str());
// printf(" 2: %s\n", cam_pos.to_string_aligned().c_str());
// printf(" origin: %s\n", (request.origin / 4096.f).to_string_aligned().c_str());
}
@@ -1,5 +1,7 @@
#pragma once
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/opengl_renderer/foreground/Shadow3CPU.h"
#include "game/graphics/opengl_renderer/opengl_utils.h"
struct Jak1ShadowSettings {
math::Vector<float, 3> center;
@@ -38,7 +40,9 @@ class Shadow3 {
math::Vector4f top_plane, bottom_plane;
math::Vector3f light_dir;
ShadowRequest* next = nullptr;
const u8* bones = nullptr;
u32 bone_idx = 0;
u32 flags = 0;
};
struct LevelChain {
@@ -62,6 +66,8 @@ class Shadow3 {
struct {
GLuint vao = -1;
GLuint indices = -1;
GLuint debug_verts = -1;
GLuint bones_buffer = -1;
int buffer_alignment = 0;
} m_opengl;
@@ -78,4 +84,12 @@ class Shadow3 {
GLuint bottom_cap = 0;
} m_uniforms;
bool m_did_first_time_setup = false;
bool m_hacks = false;
bool m_near_plane_hack = false;
int m_debug_tri = 0;
ShadowCPUWorkspace m_cpu_workspace;
ShadowCPUOutput m_cpu_output;
FullScreenDraw m_full_screen_draw;
};
@@ -0,0 +1,230 @@
#include "Shadow3CPU.h"
#include <set>
/*
*- `xform-verts` transform mesh vertices into camera space (no perspective)
- `init-vars` transform settings to camera space
- `calc-dual-verts` project vertices to plane
- `scissor-top` (only executed if shdf03 is set), clip vertices to top plane, if above
- `scissor-edges`, clip vertices to near plane
- `find-facing-single-tris`, set face bit to indicate orientation, cull backward ones
- `find-single-edges`, find edges that, when extruded, should be drawn
- `find-facing-double-tris`, set face bit indicate orientation. double sided tris, so no culling
- `find-double-edges`, find edges to extrude from the double-sided tris
- `add-verts`
- `add-facing-single-tris`
- `add-single-edges`
- `add-double-tris`
- `add-double-edges`
*/
void transform_vertices(const ShadowCPUInput& input, ShadowCPUWorkspace* work) {
struct Bone {
math::Vector4f mat[4];
u8 pad[16 * 4];
};
static_assert(sizeof(Bone) == 128);
const tfrag3::ShadowVertex* vertex_ptr = &input.vertices->operator[](input.model->first_vertex);
math::Vector4f* out_ptr = work->vertices;
const Bone* first_bone_ptr = (const Bone*)(3 * 8 * 4 * sizeof(float) + input.bones);
for (int i = 0; i < input.model->num_one_bone_vertices; i++) {
const Bone& bone = first_bone_ptr[vertex_ptr->mats[0]];
*out_ptr = bone.mat[3] + //
bone.mat[0] * vertex_ptr->pos[0] + //
bone.mat[1] * vertex_ptr->pos[1] + //
bone.mat[2] * vertex_ptr->pos[2];
vertex_ptr++;
out_ptr++;
}
for (int i = 0; i < input.model->num_two_bone_vertices; i++) {
const Bone& bone0 = first_bone_ptr[vertex_ptr->mats[0]];
math::Vector4f p0 = bone0.mat[3] + //
bone0.mat[0] * vertex_ptr->pos[0] + //
bone0.mat[1] * vertex_ptr->pos[1] + //
bone0.mat[2] * vertex_ptr->pos[2];
p0 *= vertex_ptr->weight;
const Bone& bone1 = first_bone_ptr[vertex_ptr->mats[1]];
math::Vector4f p1 = bone1.mat[3] + //
bone1.mat[0] * vertex_ptr->pos[0] + //
bone1.mat[1] * vertex_ptr->pos[1] + //
bone1.mat[2] * vertex_ptr->pos[2];
p1 *= (1.f - vertex_ptr->weight);
*out_ptr = p0 + p1;
out_ptr++;
vertex_ptr++;
}
}
void calc_dual_verts(const ShadowCPUInput& input, ShadowCPUWorkspace* work) {
int num_verts = input.model->num_one_bone_vertices + input.model->num_two_bone_vertices;
for (int i = 0; i < num_verts; i++) {
math::Vector4f origin(input.origin.x(), input.origin.y(), input.origin.z(), 1.f);
math::Vector4f p = work->vertices[i];
math::Vector4f offset = origin - p;
math::Vector4f plane = input.bottom_plane;
work->dual_vertices[i] = p - offset * p.dot(plane) / offset.xyz().dot(plane.xyz());
}
}
void scissor_top(const ShadowCPUInput& input, ShadowCPUWorkspace* work) {
// TODO
}
void scissor_edges(const ShadowCPUInput& input, ShadowCPUWorkspace* work) {
// TODO
}
void find_facing_single_tris(const ShadowCPUInput& input,
ShadowCPUWorkspace* work,
ShadowCPUOutput* output,
const std::vector<tfrag3::ShadowTri>& tris) {
int edge_offset = input.model->num_one_bone_vertices + input.model->num_two_bone_vertices;
int num_0 = 0;
int num_1 = 0;
for (size_t i = 0; i < tris.size(); i++) {
const auto& tri = tris[i];
math::Vector3f v0 = work->vertices[tri.verts[0]].xyz();
math::Vector3f v1 = work->vertices[tri.verts[1]].xyz();
math::Vector3f v2 = work->vertices[tri.verts[2]].xyz();
math::Vector3f n = (v1 - v0).cross(v2 - v0);
bool highlight = i == input.debug_highlight_tri;
if (n.dot(input.light_dir) < 0.f) {
num_0++;
work->tri_flags[i] = 1;
output->push_index(tri.verts[0], !highlight);
output->push_index(tri.verts[1], !highlight);
output->push_index(tri.verts[2], !highlight);
} else {
num_1++;
work->tri_flags[i] = 0;
output->push_index(static_cast<int>(tri.verts[0]) + edge_offset, !highlight);
output->push_index(static_cast<int>(tri.verts[1]) + edge_offset, !highlight);
output->push_index(static_cast<int>(tri.verts[2]) + edge_offset, !highlight);
}
}
}
// void find_facing_double_tris(const ShadowCPUInput& input,
// ShadowCPUWorkspace* work,
// ShadowCPUOutput* output,
// const std::vector<tfrag3::ShadowTri>& tris) {
// int edge_offset = input.model->num_one_bone_vertices + input.model->num_two_bone_vertices;
// const int flag_offset = input.model->double_tris.size();
// int num_0 = 0;
// int num_1 = 0;
// for (size_t i = 0; i < tris.size(); i++) {
// const auto& tri = tris[i];
// math::Vector3f v0 = work->vertices[tri.verts[0]].xyz();
// math::Vector3f v1 = work->vertices[tri.verts[1]].xyz();
// math::Vector3f v2 = work->vertices[tri.verts[2]].xyz();
// math::Vector3f n = (v1 - v0).cross(v2 - v0);
// if (n.dot(input.light_dir) < 0.f) {
// num_0++;
// work->tri_flags[i + flag_offset] = 1;
//
// } else {
// num_1++;
// work->tri_flags[i + flag_offset] = 0;
// }
//
// output->push_index(tri.verts[0], false);
// output->push_index(tri.verts[1], false);
// output->push_index(tri.verts[2], false);
// output->push_index(tri.verts[1], false);
// output->push_index(tri.verts[0], false);
// output->push_index(tri.verts[2], false);
// output->push_index(static_cast<int>(tri.verts[0]) + edge_offset, false);
// output->push_index(static_cast<int>(tri.verts[1]) + edge_offset, false);
// output->push_index(static_cast<int>(tri.verts[2]) + edge_offset, false);
// output->push_index(static_cast<int>(tri.verts[1]) + edge_offset, false);
// output->push_index(static_cast<int>(tri.verts[0]) + edge_offset, false);
// output->push_index(static_cast<int>(tri.verts[2]) + edge_offset, false);
// }
// }
void find_single_edges(const ShadowCPUInput& input,
ShadowCPUWorkspace* work,
ShadowCPUOutput* output) {
int num_weird = 0;
int num_0 = 0;
int num_1 = 0;
int edge_offset = input.model->num_one_bone_vertices + input.model->num_two_bone_vertices;
for (size_t i = 0; i < input.model->single_edges.size(); i++) {
const auto& e = input.model->single_edges[i];
bool skip = false;
bool out_back = false;
if (e.tri[1] == 255) {
out_back = true;
skip = work->tri_flags[e.tri[0]] == 0;
num_weird++;
} else {
u8 f0 = work->tri_flags[e.tri[0]];
u8 f1 = work->tri_flags[e.tri[1]];
if (f0 == f1) {
skip = true;
} else {
if (f0 == 1) {
out_back = true;
num_0++;
} else {
num_1++;
}
}
}
if (!skip) {
if (out_back) {
output->push_index(e.ind[0], true);
output->push_index(static_cast<int>(e.ind[0]) + edge_offset, true);
output->push_index(static_cast<int>(e.ind[1]) + edge_offset, true);
output->push_index(e.ind[0], true);
output->push_index(static_cast<int>(e.ind[1]) + edge_offset, true);
output->push_index(e.ind[1], true);
} else {
output->push_index(e.ind[0], true);
output->push_index(static_cast<int>(e.ind[1]) + edge_offset, true);
output->push_index(static_cast<int>(e.ind[0]) + edge_offset, true);
output->push_index(e.ind[0], true);
output->push_index(e.ind[1], true);
output->push_index(static_cast<int>(e.ind[1]) + edge_offset, true);
}
}
}
}
void find_facing_double_tris() {}
void find_double_edges() {}
void calc_shadow_indices(const ShadowCPUInput& input,
ShadowCPUWorkspace* work,
ShadowCPUOutput* output) {
output->num_indices = 0;
output->num_f0_indices = 0;
output->num_f1_indices = 0;
// HACK
for (auto& f : work->tri_flags) {
f = 77;
}
transform_vertices(input, work);
calc_dual_verts(input, work);
scissor_top(input, work);
scissor_edges(input, work);
find_facing_single_tris(input, work, output, input.model->single_tris);
find_single_edges(input, work, output);
// find_facing_double_tris(input, work, output, input.model->double_tris);
for (int i = 0; i < output->num_indices; i++) {
output->indices[i] += input.model->first_vertex;
}
}
@@ -0,0 +1,45 @@
#pragma once
#include "common/custom_data/Tfrag3Data.h"
#include "common/math/Vector.h"
struct ShadowCPUInput {
math::Vector3f origin;
math::Vector4f top_plane, bottom_plane;
math::Vector3f light_dir;
const u8* bones = nullptr;
const tfrag3::ShadowModel* model = nullptr;
std::vector<tfrag3::ShadowVertex>* vertices = nullptr;
u32 flags = 0;
int debug_highlight_tri = 0;
};
struct ShadowCPUOutput {
static constexpr int kMaxIndices = (256 * 3) + (256 * 3 * 2);
void push_index(u32 i, bool facing) {
indices[num_indices++] = i;
if (!facing) {
f0_indices[num_f0_indices++] = i;
} else {
f1_indices[num_f1_indices++] = i;
}
}
int num_indices = 0;
u32 indices[kMaxIndices];
int num_f0_indices = 0;
u32 f0_indices[kMaxIndices];
int num_f1_indices = 0;
int f1_indices[kMaxIndices];
};
struct ShadowCPUWorkspace {
math::Vector4f vertices[tfrag3::ShadowModel::kMaxVertices];
math::Vector4f dual_vertices[tfrag3::ShadowModel::kMaxVertices];
u8 tri_flags[tfrag3::ShadowModel::kMaxTris];
};
void calc_shadow_indices(const ShadowCPUInput& input,
ShadowCPUWorkspace* work,
ShadowCPUOutput* output);
@@ -504,7 +504,6 @@ void Loader::update(TexturePool& texture_pool) {
m_garbage_buffers.push_back(lev->collide_vertices);
m_garbage_buffers.push_back(lev->merc_vertices);
m_garbage_buffers.push_back(lev->merc_indices);
m_garbage_buffers.push_back(lev->shadow_indices);
m_garbage_buffers.push_back(lev->shadow_vertices);
for (auto& model : lev->level->merc_data.models) {
@@ -693,7 +693,6 @@ ShadowLoaderStage::ShadowLoaderStage() : LoaderStage("shadow") {}
void ShadowLoaderStage::reset() {
m_done = false;
m_opengl = false;
m_vtx_uploaded = false;
m_idx = 0;
}
@@ -703,12 +702,6 @@ bool ShadowLoaderStage::run(Timer& /*timer*/, LoaderInput& data) {
}
if (!m_opengl) {
glGenBuffers(1, &data.lev_data->shadow_indices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.lev_data->shadow_indices);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
data.lev_data->level->shadow_data.indices.size() * sizeof(u32), nullptr,
GL_STATIC_DRAW);
glGenBuffers(1, &data.lev_data->shadow_vertices);
glBindBuffer(GL_ARRAY_BUFFER, data.lev_data->shadow_vertices);
glBufferData(GL_ARRAY_BUFFER,
@@ -717,20 +710,6 @@ bool ShadowLoaderStage::run(Timer& /*timer*/, LoaderInput& data) {
m_opengl = true;
}
if (!m_vtx_uploaded) {
u32 start = m_idx;
m_idx = std::min(start + 32768, (u32)data.lev_data->level->shadow_data.indices.size());
glBindBuffer(GL_ARRAY_BUFFER, data.lev_data->shadow_indices);
glBufferSubData(GL_ARRAY_BUFFER, start * sizeof(u32), (m_idx - start) * sizeof(u32),
data.lev_data->level->shadow_data.indices.data() + start);
if (m_idx != data.lev_data->level->shadow_data.indices.size()) {
return false;
} else {
m_idx = 0;
m_vtx_uploaded = true;
}
}
u32 start = m_idx;
m_idx = std::min(start + 32768, (u32)data.lev_data->level->shadow_data.vertices.size());
glBindBuffer(GL_ARRAY_BUFFER, data.lev_data->shadow_vertices);
@@ -27,6 +27,5 @@ public:
private:
bool m_done = false;
bool m_opengl = false;
bool m_vtx_uploaded = false;
u32 m_idx = 0;
};
@@ -29,7 +29,6 @@ struct LevelData {
std::unordered_map<std::string, const tfrag3::MercModel*> merc_model_lookup;
GLuint shadow_vertices;
GLuint shadow_indices;
std::unordered_map<std::string, const tfrag3::ShadowModel*> shadow_model_lookup;
GLuint hfrag_vertices;
@@ -51,6 +51,7 @@ vec4 dual(vec4 p, vec4 plane) {
}
vec4 scissor(vec4 p, vec4 plane) {
return p;
float plane_offset = dot(p, plane);
if (plane_offset > 0) {
vec4 offset = vec4(origin, 1) - p;
@@ -62,26 +63,30 @@ vec4 scissor(vec4 p, vec4 plane) {
void main() {
vec4 p = vec4(position_in, 1);
vec4 vtx_pos;
vec4 vtx_pos = -bones[mats[0] + offset].X * p * weight_in;
if (weight_in > 1) {
vtx_pos += -bones[mats[1] + offset].X * p * (1.f - weight_in);
}
if (bottom_cap) {
vtx_pos = dual(vtx_pos, bottom_plane);
if (mats[0] == 255) {
// debug hack!
vtx_pos = vec4(position_in, weight_in);
} else {
if ((flags & uint(1)) != 0) {
vtx_pos = -bones[mats[0] + offset].X * p * weight_in;
if (weight_in > 1) {
vtx_pos += -bones[mats[1] + offset].X * p * (1.f - weight_in);
}
if (bottom_cap) {
vtx_pos = dual(vtx_pos, bottom_plane);
} else {
vtx_pos = scissor(vtx_pos, top_plane);
if ((flags & uint(1)) != 0) {
vtx_pos = dual(vtx_pos, bottom_plane);
} else {
vtx_pos = scissor(vtx_pos, top_plane);
}
}
}
vec4 transformed = perspective_matrix * vtx_pos;
float Q = fog_constants.x / transformed[3];
@@ -98,5 +103,5 @@ void main() {
gl_Position = transformed;
vtx_color = vec4(debug_color, 1.0);
vtx_color = vec4(debug_color, 0.5);
}