|
@@ -4,205 +4,19 @@
|
|
|
// http://www.anki3d.org/LICENSE
|
|
// http://www.anki3d.org/LICENSE
|
|
|
|
|
|
|
|
#include "Exporter.h"
|
|
#include "Exporter.h"
|
|
|
-#include "../../src/anki/resource/MeshLoader.h"
|
|
|
|
|
|
|
+#include <anki/resource/MeshLoader.h>
|
|
|
|
|
+#include <anki/Math.h>
|
|
|
#include <cmath>
|
|
#include <cmath>
|
|
|
-
|
|
|
|
|
-enum class ComponentFormat : uint32_t
|
|
|
|
|
-{
|
|
|
|
|
- NONE,
|
|
|
|
|
-
|
|
|
|
|
- R8,
|
|
|
|
|
- R8G8,
|
|
|
|
|
- R8G8B8,
|
|
|
|
|
- R8G8B8A8,
|
|
|
|
|
-
|
|
|
|
|
- R16,
|
|
|
|
|
- R16G16,
|
|
|
|
|
- R16G16B16,
|
|
|
|
|
- R16G16B16A16,
|
|
|
|
|
-
|
|
|
|
|
- R32,
|
|
|
|
|
- R32G32,
|
|
|
|
|
- R32G32B32,
|
|
|
|
|
- R32G32B32A32,
|
|
|
|
|
-
|
|
|
|
|
- R10G10B10A2,
|
|
|
|
|
-
|
|
|
|
|
- COUNT
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-enum class FormatTransform : uint32_t
|
|
|
|
|
-{
|
|
|
|
|
- NONE,
|
|
|
|
|
-
|
|
|
|
|
- UNORM,
|
|
|
|
|
- SNORM,
|
|
|
|
|
- UINT,
|
|
|
|
|
- SINT,
|
|
|
|
|
- FLOAT,
|
|
|
|
|
-
|
|
|
|
|
- COUNT
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct Format
|
|
|
|
|
-{
|
|
|
|
|
- ComponentFormat m_components = ComponentFormat::NONE;
|
|
|
|
|
- FormatTransform m_transform = FormatTransform::NONE;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const uint32_t FLAG_QUADS = 1;
|
|
|
|
|
-
|
|
|
|
|
-struct Header
|
|
|
|
|
-{
|
|
|
|
|
- char m_magic[8]; ///< Magic word.
|
|
|
|
|
- uint32_t m_flags;
|
|
|
|
|
- uint32_t m_flags2;
|
|
|
|
|
- Format m_positionsFormat;
|
|
|
|
|
- Format m_normalsFormat;
|
|
|
|
|
- Format m_tangentsFormat;
|
|
|
|
|
- Format m_colorsFormat; ///< Vertex color.
|
|
|
|
|
- Format m_uvsFormat;
|
|
|
|
|
- Format m_boneWeightsFormat;
|
|
|
|
|
- Format m_boneIndicesFormat;
|
|
|
|
|
- Format m_indicesFormat; ///< Vertex indices.
|
|
|
|
|
-
|
|
|
|
|
- uint32_t m_totalIndicesCount;
|
|
|
|
|
- uint32_t m_totalVerticesCount;
|
|
|
|
|
- uint32_t m_uvsChannelCount;
|
|
|
|
|
- uint32_t m_subMeshCount;
|
|
|
|
|
-
|
|
|
|
|
- uint8_t m_padding[32];
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct SubMesh
|
|
|
|
|
-{
|
|
|
|
|
- uint32_t m_firstIndex = 0;
|
|
|
|
|
- uint32_t m_indicesCount = 0;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct Vertex
|
|
|
|
|
-{
|
|
|
|
|
- float m_position[3];
|
|
|
|
|
- uint16_t m_uv[2];
|
|
|
|
|
- uint32_t m_normal;
|
|
|
|
|
- uint32_t m_tangent;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-struct BoneVertex : Vertex
|
|
|
|
|
-{
|
|
|
|
|
- uint8_t m_weights[4] = {0, 0, 0, 0};
|
|
|
|
|
- uint16_t m_boneIndices[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-static_assert(sizeof(BoneVertex) == 9 * 4, "Wrong size");
|
|
|
|
|
-
|
|
|
|
|
-static uint16_t toF16(float f)
|
|
|
|
|
-{
|
|
|
|
|
- union Val32
|
|
|
|
|
- {
|
|
|
|
|
- int32_t i;
|
|
|
|
|
- float f;
|
|
|
|
|
- uint32_t u;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- uint16_t out;
|
|
|
|
|
- Val32 v32;
|
|
|
|
|
- v32.f = f;
|
|
|
|
|
- int32_t i = v32.i;
|
|
|
|
|
- int32_t s = (i >> 16) & 0x00008000;
|
|
|
|
|
- int32_t e = ((i >> 23) & 0x000000ff) - (127 - 15);
|
|
|
|
|
- int32_t m = i & 0x007fffff;
|
|
|
|
|
-
|
|
|
|
|
- if(e <= 0)
|
|
|
|
|
- {
|
|
|
|
|
- if(e < -10)
|
|
|
|
|
- {
|
|
|
|
|
- out = 0;
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- m = (m | 0x00800000) >> (1 - e);
|
|
|
|
|
-
|
|
|
|
|
- if(m & 0x00001000)
|
|
|
|
|
- {
|
|
|
|
|
- m += 0x00002000;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- out = s | (m >> 13);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- else if(e == 0xff - (127 - 15))
|
|
|
|
|
- {
|
|
|
|
|
- if(m == 0)
|
|
|
|
|
- {
|
|
|
|
|
- out = s | 0x7c00;
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- m >>= 13;
|
|
|
|
|
- out = s | 0x7c00 | m | (m == 0);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- if(m & 0x00001000)
|
|
|
|
|
- {
|
|
|
|
|
- m += 0x00002000;
|
|
|
|
|
-
|
|
|
|
|
- if(m & 0x00800000)
|
|
|
|
|
- {
|
|
|
|
|
- m = 0;
|
|
|
|
|
- e += 1;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if(e > 30)
|
|
|
|
|
- {
|
|
|
|
|
- assert(0 && "Overflow");
|
|
|
|
|
- out = s | 0x7c00;
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- out = s | (e << 10) | (m >> 13);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return out;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-union SignedR10G10B10A10
|
|
|
|
|
-{
|
|
|
|
|
- struct
|
|
|
|
|
- {
|
|
|
|
|
- int m_x : 10;
|
|
|
|
|
- int m_y : 10;
|
|
|
|
|
- int m_z : 10;
|
|
|
|
|
- int m_w : 2;
|
|
|
|
|
- } m_unpacked;
|
|
|
|
|
- uint32_t m_packed;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-uint32_t toR10G10B10A2Sint(float r, float g, float b, float a)
|
|
|
|
|
-{
|
|
|
|
|
- SignedR10G10B10A10 out;
|
|
|
|
|
- out.m_unpacked.m_x = int(round(r * 511.0));
|
|
|
|
|
- out.m_unpacked.m_y = int(round(g * 511.0));
|
|
|
|
|
- out.m_unpacked.m_z = int(round(b * 511.0));
|
|
|
|
|
- out.m_unpacked.m_w = int(round(a * 1.0));
|
|
|
|
|
-
|
|
|
|
|
- return out.m_packed;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+#include <cfloat>
|
|
|
|
|
|
|
|
void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsigned vertCountPerFace) const
|
|
void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsigned vertCountPerFace) const
|
|
|
{
|
|
{
|
|
|
std::string name = mesh.mName.C_Str();
|
|
std::string name = mesh.mName.C_Str();
|
|
|
- std::fstream file;
|
|
|
|
|
LOGI("Exporting mesh %s", name.c_str());
|
|
LOGI("Exporting mesh %s", name.c_str());
|
|
|
|
|
|
|
|
- // Open file
|
|
|
|
|
- file.open(m_outputDirectory + name + ".ankimesh", std::ios::out | std::ios::binary);
|
|
|
|
|
|
|
+ const bool hasBoneWeights = mesh.mNumBones > 0;
|
|
|
|
|
|
|
|
- Header header;
|
|
|
|
|
|
|
+ anki::MeshBinaryFile::Header header;
|
|
|
memset(&header, 0, sizeof(header));
|
|
memset(&header, 0, sizeof(header));
|
|
|
|
|
|
|
|
// Checks
|
|
// Checks
|
|
@@ -236,87 +50,231 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
|
|
|
ERROR("Missing UVs");
|
|
ERROR("Missing UVs");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Write header
|
|
|
|
|
- static const char* magic = "ANKIMES3";
|
|
|
|
|
- memcpy(&header.m_magic, magic, 8);
|
|
|
|
|
-
|
|
|
|
|
- if(vertCountPerFace == 4)
|
|
|
|
|
|
|
+ //
|
|
|
|
|
+ // Gather the attributes
|
|
|
|
|
+ //
|
|
|
|
|
+ struct WeightVertex
|
|
|
{
|
|
{
|
|
|
- header.m_flags = FLAG_QUADS;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ uint16_t m_boneIndices[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
|
|
|
|
|
+ uint8_t m_weights[4] = {0, 0, 0, 0};
|
|
|
|
|
+ };
|
|
|
|
|
+ std::vector<WeightVertex> bweights;
|
|
|
|
|
|
|
|
- header.m_positionsFormat.m_components = ComponentFormat::R32G32B32;
|
|
|
|
|
- header.m_positionsFormat.m_transform = FormatTransform::FLOAT;
|
|
|
|
|
|
|
+ std::vector<float> positions;
|
|
|
|
|
|
|
|
- header.m_normalsFormat.m_components = ComponentFormat::R10G10B10A2;
|
|
|
|
|
- header.m_normalsFormat.m_transform = FormatTransform::SNORM;
|
|
|
|
|
|
|
+ struct NTVertex
|
|
|
|
|
+ {
|
|
|
|
|
+ float m_n[3];
|
|
|
|
|
+ float m_t[4];
|
|
|
|
|
+ float m_uv[2];
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- header.m_tangentsFormat.m_components = ComponentFormat::R10G10B10A2;
|
|
|
|
|
- header.m_tangentsFormat.m_transform = FormatTransform::SNORM;
|
|
|
|
|
|
|
+ std::vector<NTVertex> ntVerts;
|
|
|
|
|
|
|
|
- header.m_uvsFormat.m_components = ComponentFormat::R16G16;
|
|
|
|
|
- header.m_uvsFormat.m_transform = FormatTransform::FLOAT;
|
|
|
|
|
|
|
+ float maxPositionDistance = 0.0; // Distance of positions from zero
|
|
|
|
|
+ float maxUvDistance = -FLT_MAX, minUvDistance = FLT_MAX;
|
|
|
|
|
|
|
|
- header.m_indicesFormat.m_components = ComponentFormat::R16;
|
|
|
|
|
- header.m_indicesFormat.m_transform = FormatTransform::UINT;
|
|
|
|
|
|
|
+ {
|
|
|
|
|
+ const aiMatrix3x3 normalMat = (transform) ? aiMatrix3x3(*transform) : aiMatrix3x3();
|
|
|
|
|
|
|
|
- header.m_totalIndicesCount = mesh.mNumFaces * vertCountPerFace;
|
|
|
|
|
- header.m_totalVerticesCount = mesh.mNumVertices;
|
|
|
|
|
- header.m_uvsChannelCount = 1;
|
|
|
|
|
- header.m_subMeshCount = 1;
|
|
|
|
|
|
|
+ const unsigned vertCount = mesh.mNumVertices;
|
|
|
|
|
|
|
|
- const bool hasBoneWeights = mesh.mNumBones > 0;
|
|
|
|
|
- if(hasBoneWeights)
|
|
|
|
|
- {
|
|
|
|
|
- header.m_boneIndicesFormat.m_components = ComponentFormat::R16G16B16A16;
|
|
|
|
|
- header.m_boneIndicesFormat.m_transform = FormatTransform::UINT;
|
|
|
|
|
|
|
+ positions.resize(vertCount * 3);
|
|
|
|
|
+ ntVerts.resize(vertCount);
|
|
|
|
|
|
|
|
- header.m_boneWeightsFormat.m_components = ComponentFormat::R8G8B8A8;
|
|
|
|
|
- header.m_boneWeightsFormat.m_transform = FormatTransform::UNORM;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ for(unsigned i = 0; i < vertCount; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ aiVector3D pos = mesh.mVertices[i];
|
|
|
|
|
+ aiVector3D n = mesh.mNormals[i];
|
|
|
|
|
+ aiVector3D t = mesh.mTangents[i];
|
|
|
|
|
+ aiVector3D b = mesh.mBitangents[i];
|
|
|
|
|
+ const aiVector3D& uv = mesh.mTextureCoords[0][i];
|
|
|
|
|
|
|
|
- file.write(reinterpret_cast<char*>(&header), sizeof(header));
|
|
|
|
|
|
|
+ if(transform)
|
|
|
|
|
+ {
|
|
|
|
|
+ pos = (*transform) * pos;
|
|
|
|
|
+ n = normalMat * n;
|
|
|
|
|
+ t = normalMat * t;
|
|
|
|
|
+ b = normalMat * b;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Gather the bone weights
|
|
|
|
|
- struct VertWeights
|
|
|
|
|
- {
|
|
|
|
|
- float m_weights[4];
|
|
|
|
|
- uint32_t m_boneIndices[4];
|
|
|
|
|
- uint32_t m_boneCount = 0;
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ if(m_flipyz)
|
|
|
|
|
+ {
|
|
|
|
|
+ static const aiMatrix4x4 toLefthanded(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
|
|
|
|
|
|
|
|
- std::vector<VertWeights> weights;
|
|
|
|
|
|
|
+ pos = toLefthanded * pos;
|
|
|
|
|
+ n = toLefthanded * n;
|
|
|
|
|
+ t = toLefthanded * t;
|
|
|
|
|
+ b = toLefthanded * b;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if(hasBoneWeights)
|
|
|
|
|
- {
|
|
|
|
|
- weights.resize(mesh.mNumVertices);
|
|
|
|
|
|
|
+ positions[i * 3 + 0] = pos.x;
|
|
|
|
|
+ positions[i * 3 + 1] = pos.y;
|
|
|
|
|
+ positions[i * 3 + 2] = pos.z;
|
|
|
|
|
+ maxPositionDistance = std::max<float>(maxPositionDistance, fabs(pos.x));
|
|
|
|
|
+ maxPositionDistance = std::max<float>(maxPositionDistance, fabs(pos.y));
|
|
|
|
|
+ maxPositionDistance = std::max<float>(maxPositionDistance, fabs(pos.z));
|
|
|
|
|
+
|
|
|
|
|
+ ntVerts[i].m_n[0] = n.x;
|
|
|
|
|
+ ntVerts[i].m_n[1] = n.y;
|
|
|
|
|
+ ntVerts[i].m_n[2] = n.z;
|
|
|
|
|
+
|
|
|
|
|
+ ntVerts[i].m_t[0] = t.x;
|
|
|
|
|
+ ntVerts[i].m_t[1] = t.y;
|
|
|
|
|
+ ntVerts[i].m_t[2] = t.z;
|
|
|
|
|
+ ntVerts[i].m_t[3] = ((n ^ t) * b < 0.0) ? 1.0 : -1.0;
|
|
|
|
|
+
|
|
|
|
|
+ ntVerts[i].m_uv[0] = uv.x;
|
|
|
|
|
+ ntVerts[i].m_uv[1] = uv.y;
|
|
|
|
|
+ maxUvDistance = std::max(maxUvDistance, std::max(uv.x, uv.y));
|
|
|
|
|
+ minUvDistance = std::min(minUvDistance, std::min(uv.x, uv.y));
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- for(unsigned i = 0; i < mesh.mNumBones; ++i)
|
|
|
|
|
|
|
+ if(hasBoneWeights)
|
|
|
{
|
|
{
|
|
|
- const aiBone& bone = *mesh.mBones[i];
|
|
|
|
|
- for(unsigned j = 0; j < bone.mNumWeights; ++j)
|
|
|
|
|
- {
|
|
|
|
|
- const aiVertexWeight& aiWeight = bone.mWeights[j];
|
|
|
|
|
- assert(aiWeight.mVertexId < weights.size());
|
|
|
|
|
- VertWeights& vert = weights[aiWeight.mVertexId];
|
|
|
|
|
|
|
+ bweights.resize(vertCount);
|
|
|
|
|
|
|
|
- if(vert.m_boneCount == 4)
|
|
|
|
|
|
|
+ for(unsigned i = 0; i < mesh.mNumBones; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ const aiBone& bone = *mesh.mBones[i];
|
|
|
|
|
+ for(unsigned j = 0; j < bone.mNumWeights; ++j)
|
|
|
{
|
|
{
|
|
|
- ERROR("Vertex has more than 4 bone weights");
|
|
|
|
|
|
|
+ const aiVertexWeight& aiWeight = bone.mWeights[j];
|
|
|
|
|
+ assert(aiWeight.mVertexId < bweights.size());
|
|
|
|
|
+
|
|
|
|
|
+ WeightVertex& vert = bweights[aiWeight.mVertexId];
|
|
|
|
|
+
|
|
|
|
|
+ unsigned idx;
|
|
|
|
|
+ if(vert.m_boneIndices[0] == 0xFFFF)
|
|
|
|
|
+ {
|
|
|
|
|
+ idx = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if(vert.m_boneIndices[1] == 0xFFFF)
|
|
|
|
|
+ {
|
|
|
|
|
+ idx = 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if(vert.m_boneIndices[2] == 0xFFFF)
|
|
|
|
|
+ {
|
|
|
|
|
+ idx = 2;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if(vert.m_boneIndices[3] == 0xFFFF)
|
|
|
|
|
+ {
|
|
|
|
|
+ idx = 3;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ ERROR("Vertex has more than 4 bone weights");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ vert.m_boneIndices[idx] = i;
|
|
|
|
|
+ vert.m_weights[idx] = aiWeight.mWeight * 0xFF;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- vert.m_boneIndices[vert.m_boneCount] = i;
|
|
|
|
|
- vert.m_weights[vert.m_boneCount] = aiWeight.mWeight;
|
|
|
|
|
- ++vert.m_boneCount;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Chose the formats of the attributes
|
|
|
|
|
+ {
|
|
|
|
|
+ // Positions
|
|
|
|
|
+ auto& posa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::POSITION];
|
|
|
|
|
+ posa.m_bufferBinding = 0;
|
|
|
|
|
+ posa.m_format = (maxPositionDistance < 2.0) ? anki::Format::R16G16B16_SFLOAT : anki::Format::R32G32B32_SFLOAT;
|
|
|
|
|
+ posa.m_relativeOffset = 0;
|
|
|
|
|
+ posa.m_scale = 1.0;
|
|
|
|
|
+
|
|
|
|
|
+ // Normals
|
|
|
|
|
+ auto& na = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::NORMAL];
|
|
|
|
|
+ na.m_bufferBinding = 1;
|
|
|
|
|
+ na.m_format = anki::Format::A2B10G10R10_SNORM_PACK32;
|
|
|
|
|
+ na.m_relativeOffset = 0;
|
|
|
|
|
+ na.m_scale = 1.0;
|
|
|
|
|
+
|
|
|
|
|
+ // Tangents
|
|
|
|
|
+ auto& ta = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::TANGENT];
|
|
|
|
|
+ ta.m_bufferBinding = 1;
|
|
|
|
|
+ ta.m_format = anki::Format::A2B10G10R10_SNORM_PACK32;
|
|
|
|
|
+ ta.m_relativeOffset = sizeof(uint32_t);
|
|
|
|
|
+ ta.m_scale = 1.0;
|
|
|
|
|
+
|
|
|
|
|
+ // UVs
|
|
|
|
|
+ auto& uva = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::UV];
|
|
|
|
|
+ uva.m_bufferBinding = 1;
|
|
|
|
|
+ if(minUvDistance >= 0.0 && maxUvDistance <= 1.0)
|
|
|
|
|
+ {
|
|
|
|
|
+ uva.m_format = anki::Format::R16G16_UNORM;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ uva.m_format = anki::Format::R16G16_SFLOAT;
|
|
|
|
|
+ }
|
|
|
|
|
+ uva.m_relativeOffset = sizeof(uint32_t) * 2;
|
|
|
|
|
+ uva.m_scale = 1.0;
|
|
|
|
|
+
|
|
|
|
|
+ // Bone weight
|
|
|
|
|
+ if(hasBoneWeights)
|
|
|
|
|
+ {
|
|
|
|
|
+ auto& bidxa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::BONE_INDICES];
|
|
|
|
|
+ bidxa.m_bufferBinding = 2;
|
|
|
|
|
+ bidxa.m_format = anki::Format::R16G16B16A16_UINT;
|
|
|
|
|
+ bidxa.m_relativeOffset = 0;
|
|
|
|
|
+ bidxa.m_scale = 1.0;
|
|
|
|
|
+
|
|
|
|
|
+ auto& wa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::BONE_WEIGHTS];
|
|
|
|
|
+ wa.m_bufferBinding = 2;
|
|
|
|
|
+ wa.m_format = anki::Format::R8G8B8A8_UNORM;
|
|
|
|
|
+ wa.m_relativeOffset = sizeof(uint16_t) * 4;
|
|
|
|
|
+ wa.m_scale = 1.0;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Arange the attributes into vert buffers
|
|
|
|
|
+ {
|
|
|
|
|
+ // First buff has positions
|
|
|
|
|
+ const auto& posa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::POSITION];
|
|
|
|
|
+ if(posa.m_format == anki::Format::R32G32B32_SFLOAT)
|
|
|
|
|
+ {
|
|
|
|
|
+ header.m_vertexBuffers[0].m_vertexStride = sizeof(float) * 3;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ header.m_vertexBuffers[0].m_vertexStride = sizeof(uint16_t) * 3;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2nd buff has normal + tangent + texcoords
|
|
|
|
|
+ header.m_vertexBuffers[1].m_vertexStride = sizeof(uint32_t) * 2 + sizeof(uint16_t) * 2;
|
|
|
|
|
+
|
|
|
|
|
+ // 3rd has bone weights
|
|
|
|
|
+ if(hasBoneWeights)
|
|
|
|
|
+ {
|
|
|
|
|
+ header.m_vertexBuffers[2].m_vertexStride = sizeof(WeightVertex);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Write some other header stuff
|
|
|
|
|
+ {
|
|
|
|
|
+ memcpy(&header.m_magic[0], anki::MeshBinaryFile::MAGIC, 8);
|
|
|
|
|
+ header.m_flags = (vertCountPerFace == 4) ? anki::MeshBinaryFile::Flag::QUAD : anki::MeshBinaryFile::Flag::NONE;
|
|
|
|
|
+ header.m_indicesFormat = anki::Format::R16_UINT;
|
|
|
|
|
+ header.m_totalIndexCount = mesh.mNumFaces * vertCountPerFace;
|
|
|
|
|
+ header.m_totalVertexCount = mesh.mNumVertices;
|
|
|
|
|
+ header.m_subMeshCount = 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Open file
|
|
|
|
|
+ std::fstream file;
|
|
|
|
|
+ file.open(m_outputDirectory + name + ".ankimesh", std::ios::out | std::ios::binary);
|
|
|
|
|
+
|
|
|
|
|
+ // Write header
|
|
|
|
|
+ file.write(reinterpret_cast<char*>(&header), sizeof(header));
|
|
|
|
|
+
|
|
|
// Write sub meshes
|
|
// Write sub meshes
|
|
|
- SubMesh smesh;
|
|
|
|
|
- smesh.m_firstIndex = 0;
|
|
|
|
|
- smesh.m_indicesCount = header.m_totalIndicesCount;
|
|
|
|
|
- file.write(reinterpret_cast<char*>(&smesh), sizeof(smesh));
|
|
|
|
|
|
|
+ {
|
|
|
|
|
+ anki::MeshBinaryFile::SubMesh smesh;
|
|
|
|
|
+ smesh.m_firstIndex = 0;
|
|
|
|
|
+ smesh.m_indexCount = header.m_totalIndexCount;
|
|
|
|
|
+
|
|
|
|
|
+ file.write(reinterpret_cast<char*>(&smesh), sizeof(smesh));
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Write indices
|
|
// Write indices
|
|
|
for(unsigned i = 0; i < mesh.mNumFaces; i++)
|
|
for(unsigned i = 0; i < mesh.mNumFaces; i++)
|
|
@@ -343,82 +301,77 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Write vertices
|
|
|
|
|
- aiMatrix3x3 normalMat(*transform);
|
|
|
|
|
-
|
|
|
|
|
- for(unsigned i = 0; i < mesh.mNumVertices; i++)
|
|
|
|
|
|
|
+ // Write first vert buffer
|
|
|
{
|
|
{
|
|
|
- aiVector3D pos = mesh.mVertices[i];
|
|
|
|
|
- aiVector3D n = mesh.mNormals[i];
|
|
|
|
|
- aiVector3D t = mesh.mTangents[i];
|
|
|
|
|
- aiVector3D b = mesh.mBitangents[i];
|
|
|
|
|
- const aiVector3D& uv = mesh.mTextureCoords[0][i];
|
|
|
|
|
-
|
|
|
|
|
- if(transform)
|
|
|
|
|
|
|
+ const auto& posa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::POSITION];
|
|
|
|
|
+ if(posa.m_format == anki::Format::R32G32B32_SFLOAT)
|
|
|
{
|
|
{
|
|
|
- pos = (*transform) * pos;
|
|
|
|
|
- n = normalMat * n;
|
|
|
|
|
- t = normalMat * t;
|
|
|
|
|
- b = normalMat * b;
|
|
|
|
|
|
|
+ file.write(reinterpret_cast<char*>(&positions[0]), positions.size() * sizeof(positions[0]));
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- if(m_flipyz)
|
|
|
|
|
|
|
+ else if(posa.m_format == anki::Format::R16G16B16_SFLOAT)
|
|
|
{
|
|
{
|
|
|
- static const aiMatrix4x4 toLefthanded(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
|
|
|
|
|
|
|
+ std::vector<uint16_t> pos16;
|
|
|
|
|
+ pos16.resize(positions.size());
|
|
|
|
|
|
|
|
- pos = toLefthanded * pos;
|
|
|
|
|
- n = toLefthanded * n;
|
|
|
|
|
- t = toLefthanded * t;
|
|
|
|
|
- b = toLefthanded * b;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ for(unsigned i = 0; i < positions.size(); ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ pos16[i] = anki::F16(positions[i]).toU16();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- BoneVertex vert;
|
|
|
|
|
|
|
+ file.write(reinterpret_cast<char*>(&pos16[0]), pos16.size() * sizeof(pos16[0]));
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ assert(0);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Position
|
|
|
|
|
- vert.m_position[0] = pos[0];
|
|
|
|
|
- vert.m_position[1] = pos[1];
|
|
|
|
|
- vert.m_position[2] = pos[2];
|
|
|
|
|
|
|
+ // Write 2nd vert buffer
|
|
|
|
|
+ {
|
|
|
|
|
+ struct Vert
|
|
|
|
|
+ {
|
|
|
|
|
+ uint32_t m_n;
|
|
|
|
|
+ uint32_t m_t;
|
|
|
|
|
+ uint16_t m_uv[2];
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- // Tex coords
|
|
|
|
|
- vert.m_uv[0] = toF16(uv[0]);
|
|
|
|
|
- vert.m_uv[1] = toF16(uv[1]);
|
|
|
|
|
|
|
+ std::vector<Vert> verts;
|
|
|
|
|
+ verts.resize(mesh.mNumVertices);
|
|
|
|
|
|
|
|
- // Normal
|
|
|
|
|
- vert.m_normal = toR10G10B10A2Sint(n[0], n[1], n[2], 0.0);
|
|
|
|
|
|
|
+ for(unsigned i = 0; i < mesh.mNumVertices; ++i)
|
|
|
|
|
+ {
|
|
|
|
|
+ const auto& inVert = ntVerts[i];
|
|
|
|
|
|
|
|
- // Tangent
|
|
|
|
|
- float w = ((n ^ t) * b < 0.0) ? 1.0 : -1.0;
|
|
|
|
|
- vert.m_tangent = toR10G10B10A2Sint(t[0], t[1], t[2], w);
|
|
|
|
|
|
|
+ verts[i].m_n = anki::packColorToR10G10B10A2SNorm(inVert.m_n[0], inVert.m_n[1], inVert.m_n[2], 0.0);
|
|
|
|
|
+ verts[i].m_t =
|
|
|
|
|
+ anki::packColorToR10G10B10A2SNorm(inVert.m_t[0], inVert.m_t[1], inVert.m_t[2], inVert.m_t[3]);
|
|
|
|
|
|
|
|
- // Write
|
|
|
|
|
- if(hasBoneWeights)
|
|
|
|
|
- {
|
|
|
|
|
- // Normalize weights
|
|
|
|
|
- float totalWeight = 0;
|
|
|
|
|
- for(unsigned j = 0; j < weights[i].m_boneCount; ++j)
|
|
|
|
|
|
|
+ const float uv[2] = {inVert.m_uv[0], inVert.m_uv[1]};
|
|
|
|
|
+ const anki::Format uvfmt =
|
|
|
|
|
+ header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::UV].m_format;
|
|
|
|
|
+ if(uvfmt == anki::Format::R16G16_UNORM)
|
|
|
{
|
|
{
|
|
|
- totalWeight += weights[i].m_weights[j];
|
|
|
|
|
|
|
+ assert(uv[0] <= 1.0 && uv[0] >= 0.0 && uv[1] <= 1.0 && uv[1] >= 0.0);
|
|
|
|
|
+ verts[i].m_uv[0] = uv[0] * 0xFFFF;
|
|
|
|
|
+ verts[i].m_uv[1] = uv[1] * 0xFFFF;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- if(totalWeight > 0.0)
|
|
|
|
|
|
|
+ else if(uvfmt == anki::Format::R16G16_SFLOAT)
|
|
|
{
|
|
{
|
|
|
- for(unsigned j = 0; j < weights[i].m_boneCount; ++j)
|
|
|
|
|
- {
|
|
|
|
|
- weights[i].m_weights[j] /= totalWeight;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ verts[i].m_uv[0] = anki::F16(uv[0]).toU16();
|
|
|
|
|
+ verts[i].m_uv[1] = anki::F16(uv[1]).toU16();
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- for(unsigned j = 0; j < weights[i].m_boneCount; ++j)
|
|
|
|
|
|
|
+ else
|
|
|
{
|
|
{
|
|
|
- vert.m_boneIndices[j] = weights[i].m_boneIndices[j];
|
|
|
|
|
- vert.m_weights[j] = weights[i].m_weights[j] * 0xFF;
|
|
|
|
|
|
|
+ assert(0);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- file.write(reinterpret_cast<char*>(&vert), sizeof(BoneVertex));
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- file.write(reinterpret_cast<char*>(&vert), sizeof(Vertex));
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ file.write(reinterpret_cast<char*>(&verts[0]), verts.size() * sizeof(verts[0]));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Write 3rd vert buffer
|
|
|
|
|
+ if(hasBoneWeights)
|
|
|
|
|
+ {
|
|
|
|
|
+ file.write(reinterpret_cast<char*>(&bweights[0]), bweights.size() * sizeof(bweights[0]));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|