Extension of data export to GLB/GLTF format
Allows to export unlimited (more than 4) bones per vertex Use JOINTS_1,2,.. and WEIGHTS_1,2,... Added AI_CONFIG_EXPORT_GLTF_UNLIMITED_SKINNING_BONES_PER_VERTEX flagpull/5265/head^2
parent
5b8cfa920b
commit
8fcc65a8af
|
@ -172,22 +172,6 @@ static void IdentityMatrix4(mat4 &o) {
|
||||||
o[15] = 1;
|
o[15] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsBoneWeightFitted(vec4 &weight) {
|
|
||||||
return weight[0] + weight[1] + weight[2] + weight[3] >= 1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int FitBoneWeight(vec4 &weight, float value) {
|
|
||||||
int i = 0;
|
|
||||||
for (; i < 4; ++i) {
|
|
||||||
if (weight[i] < value) {
|
|
||||||
weight[i] = value;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void SetAccessorRange(Ref<Accessor> acc, void *data, size_t count,
|
void SetAccessorRange(Ref<Accessor> acc, void *data, size_t count,
|
||||||
unsigned int numCompsIn, unsigned int numCompsOut) {
|
unsigned int numCompsIn, unsigned int numCompsOut) {
|
||||||
|
@ -1009,23 +993,29 @@ Ref<Node> FindSkeletonRootJoint(Ref<Skin> &skinRef) {
|
||||||
return parentNodeRef;
|
return parentNodeRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct boneIndexWeightPair {
|
||||||
|
unsigned int indexJoint;
|
||||||
|
float weight;
|
||||||
|
bool operator()(boneIndexWeightPair &a, boneIndexWeightPair &b) {
|
||||||
|
return a.weight > b.weight;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buffer> &bufferRef, Ref<Skin> &skinRef,
|
void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buffer> &bufferRef, Ref<Skin> &skinRef,
|
||||||
std::vector<aiMatrix4x4> &inverseBindMatricesData) {
|
std::vector<aiMatrix4x4> &inverseBindMatricesData, bool unlimitedBonesPerVertex) {
|
||||||
if (aimesh->mNumBones < 1) {
|
if (aimesh->mNumBones < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the vertex joint and weight data.
|
// Store the vertex joint and weight data.
|
||||||
const size_t NumVerts(aimesh->mNumVertices);
|
const size_t NumVerts(aimesh->mNumVertices);
|
||||||
vec4 *vertexJointData = new vec4[NumVerts];
|
|
||||||
vec4 *vertexWeightData = new vec4[NumVerts];
|
|
||||||
int *jointsPerVertex = new int[NumVerts];
|
int *jointsPerVertex = new int[NumVerts];
|
||||||
|
std::vector<std::vector<boneIndexWeightPair>> allVerticesPairs;
|
||||||
|
int maxJointsPerVertex = 0;
|
||||||
for (size_t i = 0; i < NumVerts; ++i) {
|
for (size_t i = 0; i < NumVerts; ++i) {
|
||||||
jointsPerVertex[i] = 0;
|
jointsPerVertex[i] = 0;
|
||||||
for (size_t j = 0; j < 4; ++j) {
|
std::vector<boneIndexWeightPair> vertexPair;
|
||||||
vertexJointData[i][j] = 0;
|
allVerticesPairs.push_back(vertexPair);
|
||||||
vertexWeightData[i][j] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int idx_bone = 0; idx_bone < aimesh->mNumBones; ++idx_bone) {
|
for (unsigned int idx_bone = 0; idx_bone < aimesh->mNumBones; ++idx_bone) {
|
||||||
|
@ -1055,61 +1045,88 @@ void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buf
|
||||||
jointNamesIndex = static_cast<unsigned int>(inverseBindMatricesData.size() - 1);
|
jointNamesIndex = static_cast<unsigned int>(inverseBindMatricesData.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// aib->mWeights =====> vertexWeightData
|
// aib->mWeights =====> temp pairs data
|
||||||
for (unsigned int idx_weights = 0; idx_weights < aib->mNumWeights; ++idx_weights) {
|
for (unsigned int idx_weights = 0; idx_weights < aib->mNumWeights;
|
||||||
|
++idx_weights) {
|
||||||
unsigned int vertexId = aib->mWeights[idx_weights].mVertexId;
|
unsigned int vertexId = aib->mWeights[idx_weights].mVertexId;
|
||||||
float vertWeight = aib->mWeights[idx_weights].mWeight;
|
float vertWeight = aib->mWeights[idx_weights].mWeight;
|
||||||
|
allVerticesPairs[vertexId].push_back({jointNamesIndex, vertWeight});
|
||||||
// A vertex can only have at most four joint weights, which ideally sum up to 1
|
jointsPerVertex[vertexId] += 1;
|
||||||
if (IsBoneWeightFitted(vertexWeightData[vertexId])) {
|
maxJointsPerVertex =
|
||||||
continue;
|
std::max(maxJointsPerVertex, jointsPerVertex[vertexId]);
|
||||||
}
|
|
||||||
if (jointsPerVertex[vertexId] > 3) {
|
|
||||||
int boneIndexFitted = FitBoneWeight(vertexWeightData[vertexId], vertWeight);
|
|
||||||
if (boneIndexFitted != -1) {
|
|
||||||
vertexJointData[vertexId][boneIndexFitted] = static_cast<float>(jointNamesIndex);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vertexJointData[vertexId][jointsPerVertex[vertexId]] = static_cast<float>(jointNamesIndex);
|
|
||||||
vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight;
|
|
||||||
|
|
||||||
jointsPerVertex[vertexId] += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // End: for-loop mNumMeshes
|
} // End: for-loop mNumMeshes
|
||||||
|
|
||||||
Mesh::Primitive &p = meshRef->primitives.back();
|
if (!unlimitedBonesPerVertex){
|
||||||
Ref<Accessor> vertexJointAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices,
|
// skinning limited only for 4 bones per vertex, default
|
||||||
vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
|
maxJointsPerVertex = 4;
|
||||||
if (vertexJointAccessor) {
|
|
||||||
size_t offset = vertexJointAccessor->bufferView->byteOffset;
|
|
||||||
size_t bytesLen = vertexJointAccessor->bufferView->byteLength;
|
|
||||||
unsigned int s_bytesPerComp = ComponentTypeSize(ComponentType_UNSIGNED_SHORT);
|
|
||||||
unsigned int bytesPerComp = ComponentTypeSize(vertexJointAccessor->componentType);
|
|
||||||
size_t s_bytesLen = bytesLen * s_bytesPerComp / bytesPerComp;
|
|
||||||
Ref<Buffer> buf = vertexJointAccessor->bufferView->buffer;
|
|
||||||
uint8_t *arrys = new uint8_t[bytesLen];
|
|
||||||
unsigned int i = 0;
|
|
||||||
for (unsigned int j = 0; j < bytesLen; j += bytesPerComp) {
|
|
||||||
size_t len_p = offset + j;
|
|
||||||
float f_value = *(float *)&buf->GetPointer()[len_p];
|
|
||||||
unsigned short c = static_cast<unsigned short>(f_value);
|
|
||||||
memcpy(&arrys[i * s_bytesPerComp], &c, s_bytesPerComp);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
buf->ReplaceData_joint(offset, bytesLen, arrys, bytesLen);
|
|
||||||
vertexJointAccessor->componentType = ComponentType_UNSIGNED_SHORT;
|
|
||||||
vertexJointAccessor->bufferView->byteLength = s_bytesLen;
|
|
||||||
|
|
||||||
p.attributes.joint.push_back(vertexJointAccessor);
|
|
||||||
delete[] arrys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Accessor> vertexWeightAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices,
|
// temp pairs data =====> vertexWeightData
|
||||||
vertexWeightData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
|
size_t numGroups = (maxJointsPerVertex - 1) / 4 + 1;
|
||||||
if (vertexWeightAccessor) {
|
vec4 *vertexJointData = new vec4[NumVerts * numGroups];
|
||||||
p.attributes.weight.push_back(vertexWeightAccessor);
|
vec4 *vertexWeightData = new vec4[NumVerts * numGroups];
|
||||||
|
for (size_t indexVertex = 0; indexVertex < NumVerts; ++indexVertex) {
|
||||||
|
// order pairs by weight for each vertex
|
||||||
|
std::sort(allVerticesPairs[indexVertex].begin(),
|
||||||
|
allVerticesPairs[indexVertex].end(),
|
||||||
|
boneIndexWeightPair());
|
||||||
|
for (size_t indexGroup = 0; indexGroup < numGroups; ++indexGroup) {
|
||||||
|
for (size_t indexJoint = 0; indexJoint < 4; ++indexJoint) {
|
||||||
|
size_t indexBone = indexGroup * 4 + indexJoint;
|
||||||
|
size_t indexData = indexVertex + NumVerts * indexGroup;
|
||||||
|
if (indexBone >= allVerticesPairs[indexVertex].size()) {
|
||||||
|
vertexJointData[indexData][indexJoint] = 0.f;
|
||||||
|
vertexWeightData[indexData][indexJoint] = 0.f;
|
||||||
|
} else {
|
||||||
|
vertexJointData[indexData][indexJoint] =
|
||||||
|
static_cast<float>(
|
||||||
|
allVerticesPairs[indexVertex][indexBone].indexJoint);
|
||||||
|
vertexWeightData[indexData][indexJoint] =
|
||||||
|
allVerticesPairs[indexVertex][indexBone].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t idx_group = 0; idx_group < numGroups; ++idx_group) {
|
||||||
|
Mesh::Primitive &p = meshRef->primitives.back();
|
||||||
|
Ref<Accessor> vertexJointAccessor = ExportData(
|
||||||
|
mAsset, skinRef->id, bufferRef, aimesh->mNumVertices,
|
||||||
|
vertexJointData + idx_group * NumVerts,
|
||||||
|
AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
|
||||||
|
if (vertexJointAccessor) {
|
||||||
|
size_t offset = vertexJointAccessor->bufferView->byteOffset;
|
||||||
|
size_t bytesLen = vertexJointAccessor->bufferView->byteLength;
|
||||||
|
unsigned int s_bytesPerComp =
|
||||||
|
ComponentTypeSize(ComponentType_UNSIGNED_SHORT);
|
||||||
|
unsigned int bytesPerComp =
|
||||||
|
ComponentTypeSize(vertexJointAccessor->componentType);
|
||||||
|
size_t s_bytesLen = bytesLen * s_bytesPerComp / bytesPerComp;
|
||||||
|
Ref<Buffer> buf = vertexJointAccessor->bufferView->buffer;
|
||||||
|
uint8_t *arrys = new uint8_t[bytesLen];
|
||||||
|
unsigned int i = 0;
|
||||||
|
for (unsigned int j = 0; j < bytesLen; j += bytesPerComp) {
|
||||||
|
size_t len_p = offset + j;
|
||||||
|
float f_value = *(float *)&buf->GetPointer()[len_p];
|
||||||
|
unsigned short c = static_cast<unsigned short>(f_value);
|
||||||
|
memcpy(&arrys[i * s_bytesPerComp], &c, s_bytesPerComp);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
buf->ReplaceData_joint(offset, bytesLen, arrys, bytesLen);
|
||||||
|
vertexJointAccessor->componentType = ComponentType_UNSIGNED_SHORT;
|
||||||
|
vertexJointAccessor->bufferView->byteLength = s_bytesLen;
|
||||||
|
|
||||||
|
p.attributes.joint.push_back(vertexJointAccessor);
|
||||||
|
delete[] arrys;
|
||||||
|
}
|
||||||
|
Ref<Accessor> vertexWeightAccessor = ExportData(
|
||||||
|
mAsset, skinRef->id, bufferRef, aimesh->mNumVertices,
|
||||||
|
vertexWeightData + idx_group * NumVerts,
|
||||||
|
AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
|
||||||
|
if (vertexWeightAccessor) {
|
||||||
|
p.attributes.weight.push_back(vertexWeightAccessor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete[] jointsPerVertex;
|
delete[] jointsPerVertex;
|
||||||
delete[] vertexWeightData;
|
delete[] vertexWeightData;
|
||||||
|
@ -1247,9 +1264,19 @@ void glTF2Exporter::ExportMeshes() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /*************** Skins ****************/
|
||||||
|
// if (aim->HasBones()) {
|
||||||
|
// ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData);
|
||||||
|
// }
|
||||||
/*************** Skins ****************/
|
/*************** Skins ****************/
|
||||||
if (aim->HasBones()) {
|
if (aim->HasBones()) {
|
||||||
ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData);
|
bool unlimitedBonesPerVertex =
|
||||||
|
this->mProperties->HasPropertyBool(
|
||||||
|
AI_CONFIG_EXPORT_GLTF_UNLIMITED_SKINNING_BONES_PER_VERTEX) &&
|
||||||
|
this->mProperties->GetPropertyBool(
|
||||||
|
AI_CONFIG_EXPORT_GLTF_UNLIMITED_SKINNING_BONES_PER_VERTEX);
|
||||||
|
ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData,
|
||||||
|
unlimitedBonesPerVertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************** Targets for blendshapes ****************/
|
/*************** Targets for blendshapes ****************/
|
||||||
|
|
|
@ -1085,6 +1085,19 @@ enum aiComponent
|
||||||
*/
|
*/
|
||||||
#define AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS "USE_GLTF_PBR_SPECULAR_GLOSSINESS"
|
#define AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS "USE_GLTF_PBR_SPECULAR_GLOSSINESS"
|
||||||
|
|
||||||
|
/** @brief Specifies whether to apply a limit on the number of four bones per vertex in skinning
|
||||||
|
*
|
||||||
|
* When this flag is not defined, all bone weights and indices are limited to a
|
||||||
|
* maximum of four bones for each vertex (attributes JOINT_0 and WEIGHT_0 only).
|
||||||
|
* By enabling this flag, the number of bones per vertex is unlimited.
|
||||||
|
* In both cases, indices and bone weights are sorted by weight in descending order.
|
||||||
|
* In the case of the limit of up to four bones, a maximum of the four largest values are exported.
|
||||||
|
* Weights are not normalized.
|
||||||
|
* Property type: Bool. Default value: false.
|
||||||
|
*/
|
||||||
|
#define AI_CONFIG_EXPORT_GLTF_UNLIMITED_SKINNING_BONES_PER_VERTEX \
|
||||||
|
"USE_UNLIMITED_BONES_PER VERTEX"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Specifies the blob name, assimp uses for exporting.
|
* @brief Specifies the blob name, assimp uses for exporting.
|
||||||
*
|
*
|
||||||
|
|
Binary file not shown.
|
@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <assimp/commonMetaData.h>
|
#include <assimp/commonMetaData.h>
|
||||||
#include <assimp/postprocess.h>
|
#include <assimp/postprocess.h>
|
||||||
|
#include <assimp/config.h>
|
||||||
#include <assimp/scene.h>
|
#include <assimp/scene.h>
|
||||||
#include <assimp/Exporter.hpp>
|
#include <assimp/Exporter.hpp>
|
||||||
#include <assimp/Importer.hpp>
|
#include <assimp/Importer.hpp>
|
||||||
|
@ -504,6 +505,144 @@ TEST_F(utglTF2ImportExport, bug_import_simple_skin) {
|
||||||
EXPECT_NE(nullptr, scene);
|
EXPECT_NE(nullptr, scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkSkinnedScene(const aiScene *scene){
|
||||||
|
float eps = 0.001;
|
||||||
|
bool result = true;
|
||||||
|
EXPECT_EQ(scene->mNumMeshes, 1u);
|
||||||
|
EXPECT_EQ(scene->mMeshes[0]->mNumBones, 10u);
|
||||||
|
EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[0].x - -1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[0].y - -1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[0].z - 0), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[1].x - 1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[1].y - -1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[1].z - 0), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[2].x - 1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[2].y - 1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[2].z - 0), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[3].x - -1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[3].y - 1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[3].z - 0), eps);
|
||||||
|
|
||||||
|
uint numWeights[] = {4u, 4u, 4u, 4u, 2u , 1u, 1u, 2u, 1u, 1u};
|
||||||
|
float weights[10][4] = {{0.207, 0.291, 0.057, 0.303},
|
||||||
|
{0.113, 0.243, 0.499, 0.251},
|
||||||
|
{0.005, 0.010, 0.041, 0.093},
|
||||||
|
{0.090, 0.234, 0.404, 0.243},
|
||||||
|
{0.090, 0.222, 0.000, 0.000},
|
||||||
|
{0.216, 0.000, 0.000, 0.000},
|
||||||
|
{0.058, 0.000, 0.000, 0.000},
|
||||||
|
{0.086, 0.000, 0.000, 0.111},
|
||||||
|
{0.088, 0.000, 0.000, 0.000},
|
||||||
|
{0.049, 0.000, 0.000, 0.000}};
|
||||||
|
for (size_t boneIndex = 0; boneIndex < 10u; ++boneIndex) {
|
||||||
|
EXPECT_EQ(scene->mMeshes[0]->mBones[boneIndex]->mNumWeights, numWeights[boneIndex]);
|
||||||
|
std::map<uint, float> map;
|
||||||
|
for (size_t jointIndex = 0; jointIndex < scene->mMeshes[0]->mBones[boneIndex]->mNumWeights; ++jointIndex){
|
||||||
|
auto key = scene->mMeshes[0]->mBones[boneIndex]->mWeights[jointIndex].mVertexId;
|
||||||
|
auto weight = scene->mMeshes[0]->mBones[boneIndex]->mWeights[jointIndex].mWeight;
|
||||||
|
map[key] = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t jointIndex = 0; jointIndex < scene->mMeshes[0]->mBones[boneIndex]->mNumWeights; ++jointIndex) {
|
||||||
|
auto weight = map[jointIndex];
|
||||||
|
EXPECT_LT(abs(ai_real(weight) - ai_real(weights[boneIndex][jointIndex])), 0.002);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkSkinnedSceneLimited(const aiScene *scene){
|
||||||
|
float eps = 0.001;
|
||||||
|
EXPECT_EQ(scene->mNumMeshes, 1u);
|
||||||
|
EXPECT_EQ(scene->mMeshes[0]->mNumBones, 10u);
|
||||||
|
EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[0].x - -1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[0].y - -1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[0].z - 0), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[1].x - 1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[1].y - -1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[1].z - 0), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[2].x - 1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[2].y - 1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[2].z - 0), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[3].x - -1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[3].y - 1), eps);
|
||||||
|
EXPECT_LT(abs(scene->mMeshes[0]->mVertices[3].z - 0), eps);
|
||||||
|
|
||||||
|
uint numWeights[] = {4u, 4u, 1u, 4u, 1u , 1u, 1u, 1u, 1u, 1u};
|
||||||
|
float weights[10][4] = {{0.207, 0.291, 0.057, 0.303},
|
||||||
|
{0.113, 0.243, 0.499, 0.251},
|
||||||
|
{0.000, 0.000, 0.041, 0.000},
|
||||||
|
{0.090, 0.234, 0.404, 0.243},
|
||||||
|
{0.000, 0.222, 0.000, 0.000},
|
||||||
|
{0.216, 0.000, 0.000, 0.000},
|
||||||
|
{0.000, 0.000, 0.000, 0.000},
|
||||||
|
{0.000, 0.000, 0.000, 0.111},
|
||||||
|
{0.000, 0.000, 0.000, 0.000},
|
||||||
|
{0.000, 0.000, 0.000, 0.000}};
|
||||||
|
for (size_t boneIndex = 0; boneIndex < 10u; ++boneIndex) {
|
||||||
|
EXPECT_EQ(scene->mMeshes[0]->mBones[boneIndex]->mNumWeights, numWeights[boneIndex]);
|
||||||
|
std::map<uint, float> map;
|
||||||
|
for (size_t jointIndex = 0; jointIndex < scene->mMeshes[0]->mBones[boneIndex]->mNumWeights; ++jointIndex){
|
||||||
|
auto key = scene->mMeshes[0]->mBones[boneIndex]->mWeights[jointIndex].mVertexId;
|
||||||
|
auto weight = scene->mMeshes[0]->mBones[boneIndex]->mWeights[jointIndex].mWeight;
|
||||||
|
map[key] = weight;
|
||||||
|
}
|
||||||
|
for (size_t jointIndex = 0; jointIndex < scene->mMeshes[0]->mBones[boneIndex]->mNumWeights; ++jointIndex) {
|
||||||
|
auto weight = map[jointIndex];
|
||||||
|
EXPECT_LT(std::abs(ai_real(weight) - ai_real(weights[boneIndex][jointIndex])), 0.002);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(utglTF2ImportExport, bug_import_simple_skin2) {
|
||||||
|
Assimp::Importer importer;
|
||||||
|
Assimp::Exporter exporter;
|
||||||
|
const aiScene *scene = importer.ReadFile(
|
||||||
|
ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/quad_skin.glb",
|
||||||
|
aiProcess_ValidateDataStructure);
|
||||||
|
checkSkinnedScene(scene);
|
||||||
|
|
||||||
|
ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2",
|
||||||
|
ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/quad_four_out.glb"));
|
||||||
|
ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "gltf2",
|
||||||
|
ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/quad_four_out.gltf"));
|
||||||
|
|
||||||
|
// enable more than four bones per vertex
|
||||||
|
Assimp::ExportProperties properties = Assimp::ExportProperties();
|
||||||
|
properties.SetPropertyBool(
|
||||||
|
AI_CONFIG_EXPORT_GLTF_UNLIMITED_SKINNING_BONES_PER_VERTEX, true);
|
||||||
|
ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2",
|
||||||
|
ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/quad_all_out.glb", 0u, &properties));
|
||||||
|
ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "gltf2",
|
||||||
|
ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/quad_all_out.gltf", 0u, &properties));
|
||||||
|
|
||||||
|
// check skinning data of both exported files for limited number bones per vertex
|
||||||
|
const aiScene *limitedSceneImported = importer.ReadFile(
|
||||||
|
ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/quad_four_out.gltf",
|
||||||
|
aiProcess_ValidateDataStructure);
|
||||||
|
checkSkinnedSceneLimited(limitedSceneImported);
|
||||||
|
limitedSceneImported = importer.ReadFile(
|
||||||
|
ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/quad_four_out.glb",
|
||||||
|
aiProcess_ValidateDataStructure);
|
||||||
|
checkSkinnedSceneLimited(limitedSceneImported);
|
||||||
|
|
||||||
|
// check skinning data of both exported files for unlimited number bones per vertex
|
||||||
|
const aiScene *sceneImported = importer.ReadFile(
|
||||||
|
ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/quad_all_out.gltf",
|
||||||
|
aiProcess_ValidateDataStructure);
|
||||||
|
checkSkinnedScene(sceneImported);
|
||||||
|
sceneImported = importer.ReadFile(
|
||||||
|
ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/quad_all_out.glb",
|
||||||
|
aiProcess_ValidateDataStructure);
|
||||||
|
checkSkinnedScene(sceneImported);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(utglTF2ImportExport, import_cameras) {
|
TEST_F(utglTF2ImportExport, import_cameras) {
|
||||||
Assimp::Importer importer;
|
Assimp::Importer importer;
|
||||||
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/cameras/Cameras.gltf",
|
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/cameras/Cameras.gltf",
|
||||||
|
|
Loading…
Reference in New Issue