mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-10-23 20:18:10 +00:00
fix: correct XModel tangent vectors and handedness (#1046)
Model loading used binormals as tangents and derived handedness from the binormal's X component. Use the calculated tangent and basis orientation
This commit is contained in:
2 files changed
+70
-2
No files matched your search
@@ -490,7 +490,10 @@ namespace
|
||||
vertex.xyz.x = commonVertex.coordinates[0];
|
||||
vertex.xyz.y = commonVertex.coordinates[1];
|
||||
vertex.xyz.z = commonVertex.coordinates[2];
|
||||
vertex.binormalSign = binormal[0] > 0.0f ? 1.0f : -1.0f;
|
||||
const Eigen::Vector3f normal(commonVertex.normal[0], commonVertex.normal[1], commonVertex.normal[2]);
|
||||
const Eigen::Vector3f tangentVector(tangent[0], tangent[1], tangent[2]);
|
||||
const Eigen::Vector3f binormalVector(binormal[0], binormal[1], binormal[2]);
|
||||
vertex.binormalSign = normal.cross(tangentVector).dot(binormalVector) < 0.0f ? -1.0f : 1.0f;
|
||||
vertex.color = Common::Vec4PackGfxColor(commonVertex.color);
|
||||
vertex.texCoord = Common::Vec2PackTexCoords(commonVertex.uv);
|
||||
vertex.normal = Common::Vec3PackUnitVec(commonVertex.normal);
|
||||
@@ -901,7 +904,7 @@ namespace
|
||||
{
|
||||
const auto commonVertexIndex = xmodelToCommonVertexIndexLookup[vertexIndex];
|
||||
const auto& commonVertex = common.m_vertices[commonVertexIndex];
|
||||
CreateVertex(surface.verts0[vertexIndex], commonVertex, tangentData.m_binormals[commonVertexIndex], tangentData.m_binormals[commonVertexIndex]);
|
||||
CreateVertex(surface.verts0[vertexIndex], commonVertex, tangentData.m_tangents[commonVertexIndex], tangentData.m_binormals[commonVertexIndex]);
|
||||
}
|
||||
|
||||
if (!common.m_bone_weight_data.weights.empty())
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "Game/IW4/CommonIW4.h"
|
||||
#include "Gdt/GdtLookup.h"
|
||||
#include "IObjLoader.h"
|
||||
#include "SearchPath/MockSearchPath.h"
|
||||
#include "XModel/Gltf/GltfBinOutput.h"
|
||||
#include "XModel/Gltf/GltfWriter.h"
|
||||
#include "XModel/XModelCommon.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
TEST_CASE("IW4 model loading preserves tangent axes and mirrored UV handedness", "[iw4][system][xmodel]")
|
||||
{
|
||||
const auto mirrored = GENERATE(false, true);
|
||||
XModelCommon common;
|
||||
common.m_name = "triangle";
|
||||
common.m_vertices = {
|
||||
{.coordinates = {0, 0, 0}, .normal = {0, 0, 1}, .color = {1, 1, 1, 1}, .uv = {0, 0} },
|
||||
{.coordinates = {1, 0, 0}, .normal = {0, 0, 1}, .color = {1, 1, 1, 1}, .uv = {1, 0} },
|
||||
{.coordinates = {0, 1, 0}, .normal = {0, 0, 1}, .color = {1, 1, 1, 1}, .uv = {0, mirrored ? -1.0f : 1.0f}},
|
||||
};
|
||||
XModelMaterial material{};
|
||||
material.ApplyDefaults();
|
||||
material.name = "material";
|
||||
common.m_materials.push_back(material);
|
||||
common.m_objects.push_back({.name = "triangle", .materialIndex = 0, .m_faces = {XModelFace{{0, 1, 2}}}});
|
||||
std::ostringstream modelData(std::ios::binary);
|
||||
const gltf::BinOutput modelOutput(modelData);
|
||||
gltf::Writer::CreateWriter(&modelOutput, "iw4", "test")->Write(common);
|
||||
const nlohmann::json metadata{
|
||||
{"_game", "iw4" },
|
||||
{"_type", "xmodel" },
|
||||
{"_version", 2 },
|
||||
{"flags", 0 },
|
||||
{"lods", {{{"file", "triangle.glb"}, {"distance", 100}}}},
|
||||
};
|
||||
MockSearchPath searchPath;
|
||||
searchPath.AddFileData("triangle.glb", modelData.str());
|
||||
searchPath.AddFileData("xmodel/triangle.json", metadata.dump());
|
||||
searchPath.AddFileData("partclassification.csv", "");
|
||||
Zone zone("tangents", 0, GameId::IW4, GamePlatform::PC);
|
||||
AssetCreatorCollection creators(zone);
|
||||
GdtLookup gdt;
|
||||
IObjLoader::GetObjLoaderForGame(GameId::IW4)->ConfigureCreatorCollection(creators, zone, searchPath, gdt);
|
||||
IgnoredAssetLookup ignored;
|
||||
ignored.m_ignored_asset_lookup.emplace("material", IW4::ASSET_TYPE_MATERIAL);
|
||||
AssetCreationContext context(zone, &creators, &ignored);
|
||||
REQUIRE(creators.CreateAsset(IW4::ASSET_TYPE_XMODEL, "triangle", context).HasBeenSuccessful());
|
||||
for (auto i = 0u; i < 3u; ++i)
|
||||
{
|
||||
float tangent[3];
|
||||
const auto& vertex = zone.m_pools.GetAsset<IW4::AssetXModel>("triangle")->Asset()->lodInfo[0].modelSurfs->surfs[0].verts0[i];
|
||||
IW4::Common::Vec3UnpackUnitVec(vertex.tangent, tangent);
|
||||
CHECK_THAT(tangent[0], Catch::Matchers::WithinAbs(1.0f, 0.01f));
|
||||
CHECK_THAT(tangent[1], Catch::Matchers::WithinAbs(0.0f, 0.01f));
|
||||
CHECK_THAT(tangent[2], Catch::Matchers::WithinAbs(0.0f, 0.01f));
|
||||
CHECK(vertex.binormalSign == (mirrored ? -1.0f : 1.0f));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
Reference in new issue
Block a user