1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-06-07 13:47:45 +00:00

Fix tangent reading/writing

Updates to latest physis changes, and fixing tangent reading/writing.
This commit is contained in:
Joshua Goins 2023-12-17 19:00:43 -05:00
parent 9f6be07254
commit 693a47cf4e
6 changed files with 70 additions and 52 deletions

2
extern/libphysis vendored

@ -1 +1 @@
Subproject commit b439934f816fef2f07a62cf04cf892d46e1c4080 Subproject commit be79c3c9e7dedcdf7494f2ed849ce257d5421230

View file

@ -175,9 +175,10 @@ void exportModel(const QString &name, const physis_MDL &model, const physis_Skel
gltfPrimitive.attributes["TEXCOORD_0"] = gltfModel.accessors.size() + 1; gltfPrimitive.attributes["TEXCOORD_0"] = gltfModel.accessors.size() + 1;
gltfPrimitive.attributes["TEXCOORD_1"] = gltfModel.accessors.size() + 2; gltfPrimitive.attributes["TEXCOORD_1"] = gltfModel.accessors.size() + 2;
gltfPrimitive.attributes["NORMAL"] = gltfModel.accessors.size() + 3; gltfPrimitive.attributes["NORMAL"] = gltfModel.accessors.size() + 3;
gltfPrimitive.attributes["COLOR_0"] = gltfModel.accessors.size() + 6; gltfPrimitive.attributes["TANGENT"] = gltfModel.accessors.size() + 4;
gltfPrimitive.attributes["WEIGHTS_0"] = gltfModel.accessors.size() + 7; gltfPrimitive.attributes["COLOR_0"] = gltfModel.accessors.size() + 5;
gltfPrimitive.attributes["JOINTS_0"] = gltfModel.accessors.size() + 8; gltfPrimitive.attributes["WEIGHTS_0"] = gltfModel.accessors.size() + 6;
gltfPrimitive.attributes["JOINTS_0"] = gltfModel.accessors.size() + 7;
} }
mesh_offset += part.num_submeshes; mesh_offset += part.num_submeshes;
@ -211,19 +212,13 @@ void exportModel(const QString &name, const physis_MDL &model, const physis_Skel
normalAccessor.type = TINYGLTF_TYPE_VEC3; normalAccessor.type = TINYGLTF_TYPE_VEC3;
normalAccessor.byteOffset = offsetof(Vertex, normal); normalAccessor.byteOffset = offsetof(Vertex, normal);
auto &tangent1Accessor = gltfModel.accessors.emplace_back(); // We're reusing this spot for tangents (see later post-processing step)
tangent1Accessor.bufferView = gltfModel.bufferViews.size(); auto &tangentAccessor = gltfModel.accessors.emplace_back();
tangent1Accessor.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; tangentAccessor.bufferView = gltfModel.bufferViews.size();
tangent1Accessor.count = lod.parts[i].num_vertices; tangentAccessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
tangent1Accessor.type = TINYGLTF_TYPE_VEC4; tangentAccessor.count = lod.parts[i].num_vertices;
tangent1Accessor.byteOffset = offsetof(Vertex, tangent1); tangentAccessor.type = TINYGLTF_TYPE_VEC4;
tangentAccessor.byteOffset = offsetof(Vertex, bitangent);
auto &tangent2Accessor = gltfModel.accessors.emplace_back();
tangent2Accessor.bufferView = gltfModel.bufferViews.size();
tangent2Accessor.componentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
tangent2Accessor.count = lod.parts[i].num_vertices;
tangent2Accessor.type = TINYGLTF_TYPE_VEC4;
tangent2Accessor.byteOffset = offsetof(Vertex, tangent2);
auto &colorAccessor = gltfModel.accessors.emplace_back(); auto &colorAccessor = gltfModel.accessors.emplace_back();
colorAccessor.bufferView = gltfModel.bufferViews.size(); colorAccessor.bufferView = gltfModel.bufferViews.size();
@ -261,6 +256,18 @@ void exportModel(const QString &name, const physis_MDL &model, const physis_Skel
vertex.bone_id[2]++; vertex.bone_id[2]++;
vertex.bone_id[3]++; vertex.bone_id[3]++;
// Do the reverse of what we do in importing, because we need to get the tangent from the binormal.
const glm::vec3 normal = glm::vec3(vertex.normal[0], vertex.normal[1], vertex.normal[2]);
const glm::vec4 tangent = glm::vec4(vertex.bitangent[0], vertex.bitangent[1], vertex.bitangent[2], vertex.bitangent[3]);
const glm::vec3 bitangent = glm::cross(glm::vec3(tangent), normal) * tangent.w;
const float handedness = glm::dot(glm::cross(bitangent, glm::vec3(tangent)), normal) > 0 ? 1 : -1;
vertex.bitangent[0] = bitangent.x;
vertex.bitangent[1] = bitangent.y;
vertex.bitangent[2] = bitangent.z;
vertex.bitangent[3] = handedness;
newVertices.push_back(vertex); newVertices.push_back(vertex);
} }

View file

@ -84,20 +84,18 @@ void importModel(physis_MDL &existingModel, const QString &filename)
auto &mesh = model.meshes[node.mesh]; auto &mesh = model.meshes[node.mesh];
auto &primitive = mesh.primitives[0]; auto &primitive = mesh.primitives[0];
const auto getAccessor = [&model, &primitive](const std::string &name, const int index) -> unsigned char const * { const auto getAccessor = [&model, &primitive](const std::string &name, const size_t index) -> unsigned char const * {
const auto &positionAccessor = model.accessors[primitive.attributes[name]]; const auto &positionAccessor = model.accessors[primitive.attributes[name]];
const auto &positionView = model.bufferViews[positionAccessor.bufferView]; const auto &positionView = model.bufferViews[positionAccessor.bufferView];
const auto &positionBuffer = model.buffers[positionView.buffer]; const auto &positionBuffer = model.buffers[positionView.buffer];
int elementCount = tinygltf::GetNumComponentsInType(positionAccessor.type); return (positionBuffer.data.data() + (positionAccessor.ByteStride(positionView) * index) + positionView.byteOffset
int elementSize = tinygltf::GetComponentSizeInBytes(positionAccessor.componentType);
return (positionBuffer.data.data() + (std::max(positionView.byteStride, (size_t)elementCount * elementSize) * index) + positionView.byteOffset
+ positionAccessor.byteOffset); + positionAccessor.byteOffset);
}; };
// All of the accessors are mapped to the same buffer vertex view // All the accessors are mapped to the same buffer vertex view
const auto &positionAccessor = model.accessors[primitive.attributes["POSITION"]]; const auto &positionAccessor = model.accessors[primitive.attributes["POSITION"]];
const auto &colorAccessor = model.accessors[primitive.attributes["COLOR_0"]];
const auto &indexAccessor = model.accessors[primitive.indices]; const auto &indexAccessor = model.accessors[primitive.indices];
const auto &indexView = model.bufferViews[indexAccessor.bufferView]; const auto &indexView = model.bufferViews[indexAccessor.bufferView];
@ -119,12 +117,10 @@ void importModel(physis_MDL &existingModel, const QString &filename)
glm::vec2 const *uv1Data = reinterpret_cast<glm::vec2 const *>(getAccessor("TEXCOORD_1", i)); glm::vec2 const *uv1Data = reinterpret_cast<glm::vec2 const *>(getAccessor("TEXCOORD_1", i));
glm::vec4 const *weightsData = reinterpret_cast<glm::vec4 const *>(getAccessor("WEIGHTS_0", i)); glm::vec4 const *weightsData = reinterpret_cast<glm::vec4 const *>(getAccessor("WEIGHTS_0", i));
uint8_t const *jointsData = reinterpret_cast<uint8_t const *>(getAccessor("JOINTS_0", i)); uint8_t const *jointsData = reinterpret_cast<uint8_t const *>(getAccessor("JOINTS_0", i));
glm::vec4 const *tangent1Data = reinterpret_cast<glm::vec4 const *>(getAccessor("TANGENT", i));
// Replace position data // Replace position data
Vertex vertex{}; Vertex vertex{};
if (i < existingModel.lods[lodNumber].parts[partNumber].num_vertices) {
vertex = existingModel.lods[lodNumber].parts[partNumber].vertices[i];
}
vertex.position[0] = positionData->x; vertex.position[0] = positionData->x;
vertex.position[1] = positionData->y; vertex.position[1] = positionData->y;
@ -145,6 +141,34 @@ void importModel(physis_MDL &existingModel, const QString &filename)
vertex.bone_weight[2] = weightsData->z; vertex.bone_weight[2] = weightsData->z;
vertex.bone_weight[3] = weightsData->w; vertex.bone_weight[3] = weightsData->w;
// calculate binormal, because glTF won't give us those!!
const glm::vec3 normal = glm::vec3(vertex.normal[0], vertex.normal[1], vertex.normal[2]);
const glm::vec4 tangent = *tangent1Data;
const glm::vec3 bitangent = glm::cross(normal, glm::vec3(tangent)) * tangent.w;
const float handedness = glm::dot(glm::cross(glm::vec3(tangent), bitangent), normal) > 0 ? 1 : -1;
// In a cruel twist of fate, Tangent1 is actually the **BINORMAL** and not the tangent data. Square Enix is AMAZING.
vertex.bitangent[0] = bitangent.x;
vertex.bitangent[1] = bitangent.y;
vertex.bitangent[2] = bitangent.z;
vertex.bitangent[3] = handedness;
if (colorAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
unsigned short const *colorData = reinterpret_cast<unsigned short const *>(getAccessor("COLOR_0", i));
vertex.color[0] = static_cast<float>(*colorData) / std::numeric_limits<unsigned short>::max();
vertex.color[1] = static_cast<float>(*(colorData + 1)) / std::numeric_limits<unsigned short>::max();
vertex.color[2] = static_cast<float>(*(colorData + 2)) / std::numeric_limits<unsigned short>::max();
vertex.color[3] = static_cast<float>(*(colorData + 3)) / std::numeric_limits<unsigned short>::max();
} else {
glm::vec4 const *colorData = reinterpret_cast<glm::vec4 const *>(getAccessor("COLOR_0", i));
vertex.color[0] = colorData->x;
vertex.color[1] = colorData->y;
vertex.color[2] = colorData->z;
vertex.color[3] = colorData->w;
}
// We need to ensure the bones are mapped correctly // We need to ensure the bones are mapped correctly
// When exporting from modeling software, it's possible it sorted the nodes (Blender does this) // When exporting from modeling software, it's possible it sorted the nodes (Blender does this)
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {

View file

@ -7,11 +7,10 @@ layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec2 inUV0; layout(location = 1) in vec2 inUV0;
layout(location = 2) in vec2 inUV1; layout(location = 2) in vec2 inUV1;
layout(location = 3) in vec3 inNormal; layout(location = 3) in vec3 inNormal;
layout(location = 4) in uvec4 inTangent1; layout(location = 4) in vec4 inBiTangent;
layout(location = 5) in uvec4 inTangent2; layout(location = 5) in vec4 inColor;
layout(location = 6) in vec4 inColor; layout(location = 6) in vec4 inBoneWeights;
layout(location = 7) in vec4 inBoneWeights; layout(location = 7) in uvec4 inBoneIds;
layout(location = 8) in uvec4 inBoneIds;
layout(location = 0) out vec3 outNormal; layout(location = 0) out vec3 outNormal;
layout(location = 1) out vec3 outFragPos; layout(location = 1) out vec3 outFragPos;

Binary file not shown.

View file

@ -759,40 +759,28 @@ void Renderer::initPipeline()
normalAttribute.location = 3; normalAttribute.location = 3;
normalAttribute.offset = offsetof(Vertex, normal); normalAttribute.offset = offsetof(Vertex, normal);
VkVertexInputAttributeDescription tangent1Attribute = {}; VkVertexInputAttributeDescription bitangentAttribute = {};
tangent1Attribute.format = VK_FORMAT_R8G8B8A8_UINT; bitangentAttribute.format = VK_FORMAT_R32G32B32A32_SFLOAT;
tangent1Attribute.location = 4; bitangentAttribute.location = 4;
tangent1Attribute.offset = offsetof(Vertex, tangent1); bitangentAttribute.offset = offsetof(Vertex, bitangent);
VkVertexInputAttributeDescription tangent2Attribute = {};
tangent2Attribute.format = VK_FORMAT_R8G8B8A8_UINT;
tangent2Attribute.location = 5;
tangent2Attribute.offset = offsetof(Vertex, tangent2);
VkVertexInputAttributeDescription colorAttribute = {}; VkVertexInputAttributeDescription colorAttribute = {};
colorAttribute.format = VK_FORMAT_R32G32B32A32_SFLOAT; colorAttribute.format = VK_FORMAT_R32G32B32A32_SFLOAT;
colorAttribute.location = 6; colorAttribute.location = 5;
colorAttribute.offset = offsetof(Vertex, color); colorAttribute.offset = offsetof(Vertex, color);
VkVertexInputAttributeDescription boneWeightAttribute = {}; VkVertexInputAttributeDescription boneWeightAttribute = {};
boneWeightAttribute.format = VK_FORMAT_R32G32B32A32_SFLOAT; boneWeightAttribute.format = VK_FORMAT_R32G32B32A32_SFLOAT;
boneWeightAttribute.location = 7; boneWeightAttribute.location = 6;
boneWeightAttribute.offset = offsetof(Vertex, bone_weight); boneWeightAttribute.offset = offsetof(Vertex, bone_weight);
VkVertexInputAttributeDescription boneIdAttribute = {}; VkVertexInputAttributeDescription boneIdAttribute = {};
boneIdAttribute.format = VK_FORMAT_R8G8B8A8_UINT; boneIdAttribute.format = VK_FORMAT_R8G8B8A8_UINT;
boneIdAttribute.location = 8; boneIdAttribute.location = 7;
boneIdAttribute.offset = offsetof(Vertex, bone_id); boneIdAttribute.offset = offsetof(Vertex, bone_id);
const std::array attributes = {positionAttribute, const std::array attributes =
uv0Attribute, {positionAttribute, uv0Attribute, uv1Attribute, normalAttribute, bitangentAttribute, colorAttribute, boneWeightAttribute, boneIdAttribute};
uv1Attribute,
normalAttribute,
tangent1Attribute,
tangent2Attribute,
colorAttribute,
boneWeightAttribute,
boneIdAttribute};
VkPipelineVertexInputStateCreateInfo vertexInputState = {}; VkPipelineVertexInputStateCreateInfo vertexInputState = {};
vertexInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;