Skip to content

Commit

Permalink
[#83] feat(world): add XZEN (modded) support
Browse files Browse the repository at this point in the history
Co-authored-by: Luis Michaelis <me@lmichaelis.de>
  • Loading branch information
ThielHater and lmichaelis authored Aug 31, 2023
1 parent 059b782 commit cfec005
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 11 deletions.
6 changes: 5 additions & 1 deletion include/phoenix/mesh.hh
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,17 @@ namespace phoenix {
/// \param include_polygons A list of polygon indices to include in the final mesh. All other polygons are
/// discarded. This is mainly used for world meshes which include level-of-detail
/// polygons.
/// \param force_wide_indices Set to true to force 32-bit vertex indices. Useful for parsing world meshes
/// with the XZEN extension (see
/// /~https://github.com/ThielHater/GRMFixes_Union/blob/master/GRMFixes/XZenFileFormat/Plugin_Source.hpp#L86).
/// \return The parsed mesh object.
/// \note After this function returns the position of \p buf will be at the end of the parsed object.
/// If you would like to keep your buffer immutable, consider passing a copy of it to #parse(buffer&&)
/// using buffer::duplicate.
/// \throws parser_error if parsing fails.
/// \see #parse(buffer&&, const std::vector<std::uint32_t>&)
[[nodiscard]] PHOENIX_API static mesh parse(buffer& buf, std::vector<uint32_t> const& include_polygons = {});
[[nodiscard]] PHOENIX_API static mesh
parse(buffer& buf, std::vector<uint32_t> const& include_polygons = {}, bool force_wide_indices = false);

/// \brief Parses a mesh from the data in the given buffer.
///
Expand Down
18 changes: 9 additions & 9 deletions source/mesh.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace phoenix {
end = 0xB060
};

mesh mesh::parse(buffer& buf, std::vector<std::uint32_t> const& leaf_polygons) {
mesh mesh::parse(buffer& buf, std::vector<std::uint32_t> const& leaf_polygons, bool force_wide_indices) {
mesh msh {};

std::uint16_t version {};
Expand Down Expand Up @@ -117,6 +117,7 @@ namespace phoenix {
}

auto vertex_count = chunk.get();
auto has_wide_indices = (version == mesh_version_g2) || force_wide_indices;

// TODO: For meshes built for Gothic II, the `is_lod` flag can be used to determine whether a
// polygon is a leaf-polygon or not. Gothic I does not have this luxury, so the leaf polygons
Expand All @@ -127,18 +128,17 @@ namespace phoenix {
// this extra data which would grant the user more freedom in how they use _phoenix_.
if (!leaf_polygons.empty() && !std::binary_search(leaf_polygons.begin(), leaf_polygons.end(), i)) {
// If the current polygon is not a leaf polygon, skip it.
chunk.skip((version == mesh_version_g2 ? 8 : 6) * vertex_count);
chunk.skip((has_wide_indices ? 8 : 6) * vertex_count);
continue;
} else if (vertex_count == 0 || pflags.is_portal || pflags.is_ghost_occluder || pflags.is_outdoor) {
// There is no actual geometry associated with this vertex; ignore it.
chunk.skip((version == mesh_version_g2 ? 8 : 6) * vertex_count);
chunk.skip((has_wide_indices ? 8 : 6) * vertex_count);
} else if (vertex_count == 3) {
// If we have 3 vertices, we are sure that this is already a triangle,
// so we can just read it in
for (int32_t j = 0; j < vertex_count; ++j) {
msh.polygons.vertex_indices.push_back(version == mesh_version_g2 ? chunk.get_uint()
: chunk.get_ushort());

msh.polygons.vertex_indices.push_back(has_wide_indices ? chunk.get_uint()
: chunk.get_ushort());
msh.polygons.feature_indices.push_back(chunk.get_uint());
}

Expand All @@ -148,14 +148,14 @@ namespace phoenix {
} else {
// If we don't have 3 vertices, we need to calculate a triangle fan.

auto vertex_index_root = version == mesh_version_g2 ? chunk.get_uint() : chunk.get_ushort();
auto vertex_index_root = has_wide_indices ? chunk.get_uint() : chunk.get_ushort();
auto feature_index_root = chunk.get_uint();

auto vertex_index_a = version == mesh_version_g2 ? chunk.get_uint() : chunk.get_ushort();
auto vertex_index_a = has_wide_indices ? chunk.get_uint() : chunk.get_ushort();
auto feature_index_a = chunk.get_uint();

for (int32_t j = 0; j < vertex_count - 2; ++j) {
auto vertex_index_b = version == mesh_version_g2 ? chunk.get_uint() : chunk.get_ushort();
auto vertex_index_b = has_wide_indices ? chunk.get_uint() : chunk.get_ushort();
auto feature_index_b = chunk.get_uint();

msh.polygons.vertex_indices.push_back(vertex_index_root);
Expand Down
7 changes: 6 additions & 1 deletion source/world.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,13 @@ namespace phoenix {
in.skip(in.get_uint());
} while (chunk_type != 0xB060);

auto is_xzen = archive->get_header().user == "XZEN";
if (is_xzen) {
PX_LOGI("world: XZEN world detected, forcing wide vertex indices");
}

wld.world_bsp_tree = bsp_tree::parse(in, bsp_version);
wld.world_mesh = mesh::parse(mesh_data, wld.world_bsp_tree.leaf_polygons);
wld.world_mesh = mesh::parse(mesh_data, wld.world_bsp_tree.leaf_polygons, is_xzen);
} else if (chnk.object_name == "VobTree") {
auto count = archive->read_int();
wld.world_vobs.reserve(count);
Expand Down

0 comments on commit cfec005

Please sign in to comment.