+ integrate Debone-Process into Assimp. This step was contributed by mick-p, see [3262561] (https://sourceforge.net/tracker/?func=detail&aid=3262561&group_id=226462&atid=1067634)
- refactor ProcessHelper.h. Some functions now reside in ProcessHelper.cpp, which is new. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@946 67173fc5-114c-0410-ac8e-9d2fd5bffc1fpull/1/head
parent
473dae7876
commit
a6e0a5075f
4
CREDITS
4
CREDITS
|
@ -115,3 +115,7 @@ Contributed AssimpDelphi (/port/AssimpDelphi).
|
|||
|
||||
- rdb
|
||||
Contributes a bundle of fixes and improvments for the bsp-importer.
|
||||
|
||||
- Mick P
|
||||
For contributing the De-bone postprocessing step and filing various bug reports.
|
||||
|
||||
|
|
|
@ -87,7 +87,6 @@ SOURCE_GROUP( Common FILES
|
|||
BaseProcess.cpp
|
||||
BaseProcess.h
|
||||
ByteSwap.h
|
||||
ProcessHelper.h
|
||||
DefaultProgressHandler.h
|
||||
DefaultIOStream.cpp
|
||||
DefaultIOStream.h
|
||||
|
@ -362,6 +361,10 @@ SOURCE_GROUP( PostProcessing FILES
|
|||
OptimizeGraph.h
|
||||
OptimizeMeshes.cpp
|
||||
OptimizeMeshes.h
|
||||
DeboneProcess.cpp
|
||||
DeboneProcess.h
|
||||
ProcessHelper.h
|
||||
ProcessHelper.cp
|
||||
)
|
||||
|
||||
SOURCE_GROUP( Q3D FILES
|
||||
|
@ -608,6 +611,7 @@ ADD_LIBRARY( assimp SHARED
|
|||
PretransformVertices.cpp
|
||||
PretransformVertices.h
|
||||
ProcessHelper.h
|
||||
ProcessHelper.cpp
|
||||
Q3DLoader.cpp
|
||||
Q3DLoader.h
|
||||
Q3BSPFileData.h
|
||||
|
@ -734,6 +738,8 @@ ADD_LIBRARY( assimp SHARED
|
|||
Profiler.h
|
||||
NDOLoader.cpp
|
||||
NDOLoader.h
|
||||
DeboneProcess.cpp
|
||||
DeboneProcess.h
|
||||
ColladaExporter.h
|
||||
ColladaExporter.cpp
|
||||
|
||||
|
|
|
@ -0,0 +1,466 @@
|
|||
/*
|
||||
Open Asset Import Library (ASSIMP)
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2006-2010, ASSIMP Development Team
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms,
|
||||
with or without modification, are permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
* Neither the name of the ASSIMP team, nor the names of its
|
||||
contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior
|
||||
written permission of the ASSIMP Development Team.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/// @file DeboneProcess.cpp
|
||||
/** Implementation of the DeboneProcess post processing step */
|
||||
|
||||
#include "AssimpPCH.h"
|
||||
|
||||
// internal headers of the post-processing framework
|
||||
#include "ProcessHelper.h"
|
||||
#include "DeboneProcess.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
|
||||
using namespace Assimp;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Constructor to be privately used by Importer
|
||||
DeboneProcess::DeboneProcess()
|
||||
{
|
||||
mNumBones = 0;
|
||||
mNumBonesCanDoWithout = 0;
|
||||
|
||||
mThreshold = AI_DEBONE_THRESHOLD;
|
||||
mAllOrNone = false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Destructor, private as well
|
||||
DeboneProcess::~DeboneProcess()
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Returns whether the processing step is present in the given flag field.
|
||||
bool DeboneProcess::IsActive( unsigned int pFlags) const
|
||||
{
|
||||
return (pFlags & aiProcess_Debone) != 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Executes the post processing step on the given imported data.
|
||||
void DeboneProcess::SetupProperties(const Importer* pImp)
|
||||
{
|
||||
// get the current value of the property
|
||||
mAllOrNone = pImp->GetPropertyInteger(AI_CONFIG_PP_DB_ALL_OR_NONE,0)?true:false;
|
||||
mThreshold = pImp->GetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD,AI_DEBONE_THRESHOLD);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Executes the post processing step on the given imported data.
|
||||
void DeboneProcess::Execute( aiScene* pScene)
|
||||
{
|
||||
DefaultLogger::get()->debug("DeboneProcess begin");
|
||||
|
||||
if(!pScene->mNumMeshes) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<bool> splitList(pScene->mNumMeshes);
|
||||
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
|
||||
splitList[a] = ConsiderMesh( pScene->mMeshes[a] );
|
||||
}
|
||||
|
||||
int numSplits = 0;
|
||||
|
||||
if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones)) {
|
||||
for(unsigned int a = 0; a < pScene->mNumMeshes; a++) {
|
||||
if(splitList[a]) {
|
||||
numSplits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(numSplits) {
|
||||
// we need to do something. Let's go.
|
||||
mSubMeshIndices.clear();
|
||||
mSubMeshIndices.resize(pScene->mNumMeshes);
|
||||
|
||||
// build a new array of meshes for the scene
|
||||
std::vector<aiMesh*> meshes;
|
||||
|
||||
for(unsigned int a=0;a<pScene->mNumMeshes;a++)
|
||||
{
|
||||
aiMesh* srcMesh = pScene->mMeshes[a];
|
||||
|
||||
std::vector<std::pair<aiMesh*,const aiBone*> > newMeshes;
|
||||
|
||||
if(splitList[a]) {
|
||||
SplitMesh(srcMesh,newMeshes);
|
||||
}
|
||||
|
||||
// mesh was split
|
||||
if(!newMeshes.empty()) {
|
||||
unsigned int out = 0, in = srcMesh->mNumBones;
|
||||
|
||||
// store new meshes and indices of the new meshes
|
||||
for(unsigned int b=0;b<newMeshes.size();b++) {
|
||||
const aiString *find = newMeshes[b].second?&newMeshes[b].second->mName:0;
|
||||
|
||||
aiNode *theNode = find?pScene->mRootNode->FindNode(*find):0;
|
||||
std::pair<unsigned int,aiNode*> push_pair(meshes.size(),theNode);
|
||||
|
||||
mSubMeshIndices[a].push_back(push_pair);
|
||||
meshes.push_back(newMeshes[b].first);
|
||||
|
||||
out+=newMeshes[b].first->mNumBones;
|
||||
}
|
||||
|
||||
if(!DefaultLogger::isNullLogger()) {
|
||||
char buffer[1024];
|
||||
::sprintf(buffer,"Removed %i bones. Input bones: %i. Output bones: %i",in-out,in,out);
|
||||
DefaultLogger::get()->info(buffer);
|
||||
}
|
||||
|
||||
// and destroy the source mesh. It should be completely contained inside the new submeshes
|
||||
delete srcMesh;
|
||||
}
|
||||
else {
|
||||
// Mesh is kept unchanged - store it's new place in the mesh array
|
||||
mSubMeshIndices[a].push_back(std::pair<unsigned int,aiNode*>(meshes.size(),0));
|
||||
meshes.push_back(srcMesh);
|
||||
}
|
||||
}
|
||||
|
||||
// rebuild the scene's mesh array
|
||||
pScene->mNumMeshes = meshes.size();
|
||||
delete [] pScene->mMeshes;
|
||||
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
|
||||
std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
|
||||
|
||||
// recurse through all nodes and translate the node's mesh indices to fit the new mesh array
|
||||
UpdateNode( pScene->mRootNode);
|
||||
}
|
||||
|
||||
DefaultLogger::get()->debug("DeboneProcess end");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Counts bones total/removable in a given mesh.
|
||||
bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh)
|
||||
{
|
||||
if(!pMesh->HasBones()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool split = false;
|
||||
|
||||
//interstitial faces not permitted
|
||||
bool isInterstitialRequired = false;
|
||||
|
||||
std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
|
||||
std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
|
||||
|
||||
const unsigned int cUnowned = UINT_MAX;
|
||||
const unsigned int cCoowned = UINT_MAX-1;
|
||||
|
||||
for(unsigned int i=0;i<pMesh->mNumBones;i++) {
|
||||
for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++) {
|
||||
float w = pMesh->mBones[i]->mWeights[j].mWeight;
|
||||
|
||||
if(w==0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
|
||||
if(w>=mThreshold) {
|
||||
|
||||
if(vertexBones[vid]!=cUnowned) {
|
||||
if(vertexBones[vid]==i) //double entry
|
||||
{
|
||||
DefaultLogger::get()->warn("Encountered double entry in bone weights");
|
||||
}
|
||||
else //TODO: track attraction in order to break tie
|
||||
{
|
||||
vertexBones[vid] = cCoowned;
|
||||
}
|
||||
}
|
||||
else vertexBones[vid] = i;
|
||||
}
|
||||
|
||||
if(!isBoneNecessary[i]) {
|
||||
isBoneNecessary[i] = w<mThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isBoneNecessary[i]) {
|
||||
isInterstitialRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(isInterstitialRequired) {
|
||||
for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
|
||||
unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
|
||||
|
||||
for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
|
||||
unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
|
||||
|
||||
if(v!=w) {
|
||||
if(v<pMesh->mNumBones) isBoneNecessary[v] = true;
|
||||
if(w<pMesh->mNumBones) isBoneNecessary[w] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int i=0;i<pMesh->mNumBones;i++) {
|
||||
if(!isBoneNecessary[i]) {
|
||||
mNumBonesCanDoWithout++;
|
||||
split = true;
|
||||
}
|
||||
|
||||
mNumBones++;
|
||||
}
|
||||
return split;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Splits the given mesh by bone count.
|
||||
void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector<std::pair<aiMesh*,const aiBone*>>& poNewMeshes) const
|
||||
{
|
||||
// same deal here as ConsiderMesh basically
|
||||
|
||||
std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
|
||||
std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
|
||||
|
||||
const unsigned int cUnowned = UINT_MAX;
|
||||
const unsigned int cCoowned = UINT_MAX-1;
|
||||
|
||||
for(unsigned int i=0;i<pMesh->mNumBones;i++) {
|
||||
for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++) {
|
||||
float w = pMesh->mBones[i]->mWeights[j].mWeight;
|
||||
|
||||
if(w==0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
|
||||
|
||||
if(w>=mThreshold) {
|
||||
if(vertexBones[vid]!=cUnowned) {
|
||||
if(vertexBones[vid]==i) //double entry
|
||||
{
|
||||
//DefaultLogger::get()->warn("Encountered double entry in bone weights");
|
||||
}
|
||||
else //TODO: track attraction in order to break tie
|
||||
{
|
||||
vertexBones[vid] = cCoowned;
|
||||
}
|
||||
}
|
||||
else vertexBones[vid] = i;
|
||||
}
|
||||
|
||||
if(!isBoneNecessary[i]) {
|
||||
isBoneNecessary[i] = w<mThreshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int nFacesUnowned = 0;
|
||||
|
||||
std::vector<unsigned int> faceBones(pMesh->mNumFaces,UINT_MAX);
|
||||
std::vector<unsigned int> facesPerBone(pMesh->mNumBones,0);
|
||||
|
||||
for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
|
||||
unsigned int nInterstitial = 1;
|
||||
|
||||
unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
|
||||
|
||||
for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
|
||||
unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
|
||||
|
||||
if(v!=w) {
|
||||
if(v<pMesh->mNumBones) isBoneNecessary[v] = true;
|
||||
if(w<pMesh->mNumBones) isBoneNecessary[w] = true;
|
||||
}
|
||||
else nInterstitial++;
|
||||
}
|
||||
|
||||
if(v<pMesh->mNumBones &&nInterstitial==pMesh->mFaces[i].mNumIndices) {
|
||||
faceBones[i] = v; //primitive belongs to bone #v
|
||||
facesPerBone[v]++;
|
||||
}
|
||||
else nFacesUnowned++;
|
||||
}
|
||||
|
||||
// invalidate any "cojoined" faces
|
||||
for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
|
||||
if(faceBones[i]<pMesh->mNumBones&&isBoneNecessary[faceBones[i]])
|
||||
{
|
||||
ai_assert(facesPerBone[faceBones[i]]>0);
|
||||
facesPerBone[faceBones[i]]--;
|
||||
|
||||
nFacesUnowned++;
|
||||
faceBones[i] = cUnowned;
|
||||
}
|
||||
}
|
||||
|
||||
if(nFacesUnowned) {
|
||||
std::vector<unsigned int> subFaces;
|
||||
|
||||
for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
|
||||
if(faceBones[i]==cUnowned) {
|
||||
subFaces.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
aiMesh *baseMesh = MakeSubmesh(pMesh,subFaces,0);
|
||||
std::pair<aiMesh*,const aiBone*> push_pair(baseMesh,0);
|
||||
|
||||
poNewMeshes.push_back(push_pair);
|
||||
}
|
||||
|
||||
for(unsigned int i=0;i<pMesh->mNumBones;i++) {
|
||||
|
||||
if(!isBoneNecessary[i]&&facesPerBone[i]>0) {
|
||||
std::vector<unsigned int> subFaces;
|
||||
|
||||
for(unsigned int j=0;j<pMesh->mNumFaces;j++) {
|
||||
if(faceBones[j]==i) {
|
||||
subFaces.push_back(j);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int f = AI_SUBMESH_FLAGS_SANS_BONES;
|
||||
aiMesh *subMesh =MakeSubmesh(pMesh,subFaces,f);
|
||||
|
||||
//Lifted from PretransformVertices.cpp
|
||||
ApplyTransform(subMesh,pMesh->mBones[i]->mOffsetMatrix);
|
||||
std::pair<aiMesh*,const aiBone*> push_pair(subMesh,pMesh->mBones[i]);
|
||||
|
||||
poNewMeshes.push_back(push_pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Recursively updates the node's mesh list to account for the changed mesh list
|
||||
void DeboneProcess::UpdateNode(aiNode* pNode) const
|
||||
{
|
||||
// rebuild the node's mesh index list
|
||||
|
||||
std::vector<unsigned int> newMeshList;
|
||||
|
||||
// this will require two passes
|
||||
|
||||
unsigned int m = pNode->mNumMeshes, n = mSubMeshIndices.size();
|
||||
|
||||
// first pass, look for meshes which have not moved
|
||||
|
||||
for(unsigned int a=0;a<m;a++) {
|
||||
|
||||
unsigned int srcIndex = pNode->mMeshes[a];
|
||||
const std::vector<std::pair<unsigned int,aiNode*>> &subMeshes = mSubMeshIndices[srcIndex];
|
||||
unsigned int nSubmeshes = subMeshes.size();
|
||||
|
||||
for(unsigned int b=0;b<nSubmeshes;b++) {
|
||||
if(!subMeshes[b].second) {
|
||||
newMeshList.push_back(subMeshes[b].first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// second pass, collect deboned meshes
|
||||
|
||||
for(unsigned int a=0;a<n;a++)
|
||||
{
|
||||
const std::vector<std::pair<unsigned int,aiNode*>> &subMeshes = mSubMeshIndices[a];
|
||||
unsigned int nSubmeshes = subMeshes.size();
|
||||
|
||||
for(unsigned int b=0;b<nSubmeshes;b++) {
|
||||
if(subMeshes[b].second == pNode) {
|
||||
newMeshList.push_back(subMeshes[b].first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pNode->mNumMeshes > 0 ) {
|
||||
delete [] pNode->mMeshes; pNode->mMeshes = NULL;
|
||||
}
|
||||
|
||||
pNode->mNumMeshes = newMeshList.size();
|
||||
|
||||
if(pNode->mNumMeshes) {
|
||||
pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
|
||||
std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
|
||||
}
|
||||
|
||||
// do that also recursively for all children
|
||||
for( unsigned int a = 0; a < pNode->mNumChildren; ++a ) {
|
||||
UpdateNode( pNode->mChildren[a]);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Apply the node transformation to a mesh
|
||||
void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const
|
||||
{
|
||||
// Check whether we need to transform the coordinates at all
|
||||
if (!mat.IsIdentity()) {
|
||||
|
||||
if (mesh->HasPositions()) {
|
||||
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
|
||||
mesh->mVertices[i] = mat * mesh->mVertices[i];
|
||||
}
|
||||
}
|
||||
if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
|
||||
aiMatrix4x4 mWorldIT = mat;
|
||||
mWorldIT.Inverse().Transpose();
|
||||
|
||||
// TODO: implement Inverse() for aiMatrix3x3
|
||||
aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
|
||||
|
||||
if (mesh->HasNormals()) {
|
||||
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
|
||||
mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
|
||||
}
|
||||
}
|
||||
if (mesh->HasTangentsAndBitangents()) {
|
||||
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
|
||||
mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
|
||||
mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
Open Asset Import Library (ASSIMP)
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2006-2010, ASSIMP Development Team
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms,
|
||||
with or without modification, are permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
* Neither the name of the ASSIMP team, nor the names of its
|
||||
contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior
|
||||
written permission of the ASSIMP Development Team.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/** Defines a post processing step to limit the number of bones affecting a single vertex. */
|
||||
#ifndef AI_DEBONEPROCESS_H_INC
|
||||
#define AI_DEBONEPROCESS_H_INC
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include "BaseProcess.h"
|
||||
|
||||
#include "../include/aiMesh.h"
|
||||
#include "../include/aiScene.h"
|
||||
|
||||
class DeboneTest;
|
||||
|
||||
namespace Assimp
|
||||
{
|
||||
|
||||
#if (!defined AI_DEBONE_THRESHOLD)
|
||||
# define AI_DEBONE_THRESHOLD 1.0f
|
||||
#endif // !! AI_DEBONE_THRESHOLD
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
/** This post processing step removes bones nearly losslessly or according to
|
||||
* a configured threshold. In order to remove the bone, the primitives affected by
|
||||
* the bone are split from the mesh. The split off (new) mesh is boneless. At any
|
||||
* point in time, bones without affect upon a given mesh are to be removed.
|
||||
*/
|
||||
class ASSIMP_API DeboneProcess : public BaseProcess
|
||||
{
|
||||
friend class Importer;
|
||||
friend class ::DeboneTest;
|
||||
|
||||
protected:
|
||||
/** Constructor to be privately used by Importer */
|
||||
DeboneProcess();
|
||||
|
||||
/** Destructor, private as well */
|
||||
~DeboneProcess();
|
||||
|
||||
public:
|
||||
// -------------------------------------------------------------------
|
||||
/** Returns whether the processing step is present in the given flag.
|
||||
* @param pFlags The processing flags the importer was called with.
|
||||
* A bitwise combination of #aiPostProcessSteps.
|
||||
* @return true if the process is present in this flag fields,
|
||||
* false if not.
|
||||
*/
|
||||
bool IsActive( unsigned int pFlags) const;
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Called prior to ExecuteOnScene().
|
||||
* The function is a request to the process to update its configuration
|
||||
* basing on the Importer's configuration property list.
|
||||
*/
|
||||
void SetupProperties(const Importer* pImp);
|
||||
|
||||
protected:
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Executes the post processing step on the given imported data.
|
||||
* At the moment a process is not supposed to fail.
|
||||
* @param pScene The imported data to work at.
|
||||
*/
|
||||
void Execute( aiScene* pScene);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Counts bones total/removable in a given mesh.
|
||||
* @param pMesh The mesh to process.
|
||||
*/
|
||||
bool ConsiderMesh( const aiMesh* pMesh);
|
||||
|
||||
/// Splits the given mesh by bone count.
|
||||
/// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split.
|
||||
/// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary.
|
||||
void SplitMesh(const aiMesh* pMesh, std::vector<std::pair<aiMesh*,const aiBone*>>& poNewMeshes) const;
|
||||
|
||||
/// Recursively updates the node's mesh list to account for the changed mesh list
|
||||
void UpdateNode(aiNode* pNode) const;
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Apply transformation to a mesh
|
||||
void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const;
|
||||
|
||||
public:
|
||||
/** Number of bones present in the scene. */
|
||||
unsigned int mNumBones;
|
||||
unsigned int mNumBonesCanDoWithout;
|
||||
|
||||
float mThreshold;
|
||||
bool mAllOrNone;
|
||||
|
||||
/// Per mesh index: Array of indices of the new submeshes.
|
||||
std::vector< std::vector< std::pair< unsigned int,aiNode* > > > mSubMeshIndices;
|
||||
};
|
||||
|
||||
} // end of namespace Assimp
|
||||
|
||||
#endif // AI_DEBONEPROCESS_H_INC
|
|
@ -178,9 +178,6 @@ using namespace Assimp::Formatter;
|
|||
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
|
||||
# include "BlenderLoader.h"
|
||||
#endif
|
||||
//#ifndef ASSIMP_BUILD_NO_SWORDOFMOONLIGHT_IMPORTER
|
||||
//# include "SomLoader.h"
|
||||
//#endif
|
||||
#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
|
||||
# include "Q3BSPFileImporter.h"
|
||||
#endif
|
||||
|
@ -260,6 +257,9 @@ using namespace Assimp::Formatter;
|
|||
#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS
|
||||
# include "SplitByBoneCountProcess.h"
|
||||
#endif
|
||||
#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS
|
||||
# include "DeboneProcess.h"
|
||||
#endif
|
||||
|
||||
using namespace Assimp;
|
||||
using namespace Assimp::Intern;
|
||||
|
@ -426,9 +426,6 @@ Importer::Importer()
|
|||
#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER)
|
||||
pimpl->mImporter.push_back( new BlenderImporter());
|
||||
#endif
|
||||
//#if (!defined ASSIMP_BUILD_NO_SWORDOFMOONLIGHT_IMPORTER)
|
||||
// pimpl->mImporter.push_back( new SomImporter());
|
||||
//#endif
|
||||
#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER)
|
||||
pimpl->mImporter.push_back( new Q3BSPFileImporter() );
|
||||
#endif
|
||||
|
@ -522,6 +519,9 @@ Importer::Importer()
|
|||
#if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
|
||||
pimpl->mPostProcessingSteps.push_back( new FlipWindingOrderProcess());
|
||||
#endif
|
||||
#if (!defined ASSIMP_BUILD_DEBONE_PROCESS)
|
||||
pimpl->mPostProcessingSteps.push_back( new DeboneProcess());
|
||||
#endif
|
||||
#if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS)
|
||||
pimpl->mPostProcessingSteps.push_back( new LimitBoneWeightsProcess());
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
Open Asset Import Library (ASSIMP)
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2006-2010, ASSIMP Development Team
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms,
|
||||
with or without modification, are permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
* Neither the name of the ASSIMP team, nor the names of its
|
||||
contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior
|
||||
written permission of the ASSIMP Development Team.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/// @file ProcessHelper.cpp
|
||||
/** Implement shared utility functions for postprocessing steps */
|
||||
|
||||
#include "AssimpPCH.h"
|
||||
#include "ProcessHelper.h"
|
||||
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace Assimp {
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
void ConvertListToStrings(const std::string& in, std::list<std::string>& out)
|
||||
{
|
||||
const char* s = in.c_str();
|
||||
while (*s) {
|
||||
SkipSpacesAndLineEnd(&s);
|
||||
if (*s == '\'') {
|
||||
const char* base = ++s;
|
||||
while (*s != '\'') {
|
||||
++s;
|
||||
if (*s == '\0') {
|
||||
DefaultLogger::get()->error("ConvertListToString: String list is ill-formatted");
|
||||
return;
|
||||
}
|
||||
}
|
||||
out.push_back(std::string(base,(size_t)(s-base)));
|
||||
++s;
|
||||
}
|
||||
else {
|
||||
out.push_back(GetNextToken(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max,
|
||||
const aiMatrix4x4& m)
|
||||
{
|
||||
min = aiVector3D (10e10f, 10e10f, 10e10f);
|
||||
max = aiVector3D (-10e10f,-10e10f,-10e10f);
|
||||
for (unsigned int i = 0;i < mesh->mNumVertices;++i)
|
||||
{
|
||||
const aiVector3D v = m * mesh->mVertices[i];
|
||||
min = std::min(v,min);
|
||||
max = std::max(v,max);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
|
||||
{
|
||||
ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max);
|
||||
out = min + (max-min)*0.5f;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,
|
||||
aiVector3D& max, const aiMatrix4x4& m)
|
||||
{
|
||||
FindAABBTransformed(mesh,min,max,m);
|
||||
out = min + (max-min)*0.5f;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
|
||||
{
|
||||
aiVector3D min,max;
|
||||
FindMeshCenter(mesh,out,min,max);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,
|
||||
const aiMatrix4x4& m)
|
||||
{
|
||||
aiVector3D min,max;
|
||||
FindMeshCenterTransformed(mesh,out,min,max,m);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
float ComputePositionEpsilon(const aiMesh* pMesh)
|
||||
{
|
||||
const float epsilon = 1e-4f;
|
||||
|
||||
// calculate the position bounds so we have a reliable epsilon to check position differences against
|
||||
aiVector3D minVec, maxVec;
|
||||
ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec);
|
||||
return (maxVec - minVec).Length() * epsilon;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
float ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num)
|
||||
{
|
||||
const float epsilon = 1e-4f;
|
||||
|
||||
// calculate the position bounds so we have a reliable epsilon to check position differences against
|
||||
aiVector3D minVec, maxVec, mi, ma;
|
||||
MinMaxChooser<aiVector3D>()(minVec,maxVec);
|
||||
|
||||
for (size_t a = 0; a < num; ++a) {
|
||||
const aiMesh* pMesh = pMeshes[a];
|
||||
ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma);
|
||||
|
||||
minVec = std::min(minVec,mi);
|
||||
maxVec = std::max(maxVec,ma);
|
||||
}
|
||||
return (maxVec - minVec).Length() * epsilon;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh)
|
||||
{
|
||||
ai_assert(NULL != pcMesh);
|
||||
|
||||
// FIX: the hash may never be 0. Otherwise a comparison against
|
||||
// nullptr could be successful
|
||||
unsigned int iRet = 1;
|
||||
|
||||
// normals
|
||||
if (pcMesh->HasNormals())iRet |= 0x2;
|
||||
// tangents and bitangents
|
||||
if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4;
|
||||
|
||||
#ifdef BOOST_STATIC_ASSERT
|
||||
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
|
||||
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
|
||||
#endif
|
||||
|
||||
// texture coordinates
|
||||
unsigned int p = 0;
|
||||
while (pcMesh->HasTextureCoords(p))
|
||||
{
|
||||
iRet |= (0x100 << p);
|
||||
if (3 == pcMesh->mNumUVComponents[p])
|
||||
iRet |= (0x10000 << p);
|
||||
|
||||
++p;
|
||||
}
|
||||
// vertex colors
|
||||
p = 0;
|
||||
while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++);
|
||||
return iRet;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh)
|
||||
{
|
||||
if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VertexWeightTable* avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
|
||||
for (unsigned int i = 0; i < pMesh->mNumBones;++i) {
|
||||
|
||||
aiBone* bone = pMesh->mBones[i];
|
||||
for (unsigned int a = 0; a < bone->mNumWeights;++a) {
|
||||
const aiVertexWeight& weight = bone->mWeights[a];
|
||||
avPerVertexWeights[weight.mVertexId].push_back( std::pair<unsigned int,float>(i,weight.mWeight) );
|
||||
}
|
||||
}
|
||||
return avPerVertexWeights;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
const char* TextureTypeToString(aiTextureType in)
|
||||
{
|
||||
switch (in)
|
||||
{
|
||||
case aiTextureType_NONE:
|
||||
return "n/a";
|
||||
case aiTextureType_DIFFUSE:
|
||||
return "Diffuse";
|
||||
case aiTextureType_SPECULAR:
|
||||
return "Specular";
|
||||
case aiTextureType_AMBIENT:
|
||||
return "Ambient";
|
||||
case aiTextureType_EMISSIVE:
|
||||
return "Emissive";
|
||||
case aiTextureType_OPACITY:
|
||||
return "Opacity";
|
||||
case aiTextureType_NORMALS:
|
||||
return "Normals";
|
||||
case aiTextureType_HEIGHT:
|
||||
return "Height";
|
||||
case aiTextureType_SHININESS:
|
||||
return "Shininess";
|
||||
case aiTextureType_DISPLACEMENT:
|
||||
return "Displacement";
|
||||
case aiTextureType_LIGHTMAP:
|
||||
return "Lightmap";
|
||||
case aiTextureType_REFLECTION:
|
||||
return "Reflection";
|
||||
case aiTextureType_UNKNOWN:
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
ai_assert(false);
|
||||
return "BUG";
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
const char* MappingTypeToString(aiTextureMapping in)
|
||||
{
|
||||
switch (in)
|
||||
{
|
||||
case aiTextureMapping_UV:
|
||||
return "UV";
|
||||
case aiTextureMapping_BOX:
|
||||
return "Box";
|
||||
case aiTextureMapping_SPHERE:
|
||||
return "Sphere";
|
||||
case aiTextureMapping_CYLINDER:
|
||||
return "Cylinder";
|
||||
case aiTextureMapping_PLANE:
|
||||
return "Plane";
|
||||
case aiTextureMapping_OTHER:
|
||||
return "Other";
|
||||
}
|
||||
|
||||
ai_assert(false);
|
||||
return "BUG";
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
aiMesh* MakeSubmesh(const aiMesh *pMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags)
|
||||
{
|
||||
aiMesh *oMesh = new aiMesh();
|
||||
std::vector<unsigned int> vMap(pMesh->mNumVertices,UINT_MAX);
|
||||
|
||||
size_t numSubVerts = 0;
|
||||
size_t numSubFaces = subMeshFaces.size();
|
||||
|
||||
for(unsigned int i=0;i<numSubFaces;i++) {
|
||||
const aiFace &f = pMesh->mFaces[subMeshFaces[i]];
|
||||
|
||||
for(unsigned int j=0;j<f.mNumIndices;j++) {
|
||||
if(vMap[f.mIndices[j]]==UINT_MAX) {
|
||||
vMap[f.mIndices[j]] = numSubVerts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oMesh->mName = pMesh->mName;
|
||||
|
||||
oMesh->mMaterialIndex = pMesh->mMaterialIndex;
|
||||
oMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
|
||||
|
||||
// create all the arrays for this mesh if the old mesh contained them
|
||||
|
||||
oMesh->mNumFaces = subMeshFaces.size();
|
||||
oMesh->mNumVertices = numSubVerts;
|
||||
oMesh->mVertices = new aiVector3D[numSubVerts];
|
||||
if( pMesh->HasNormals() ) {
|
||||
oMesh->mNormals = new aiVector3D[numSubVerts];
|
||||
}
|
||||
|
||||
if( pMesh->HasTangentsAndBitangents() ) {
|
||||
oMesh->mTangents = new aiVector3D[numSubVerts];
|
||||
oMesh->mBitangents = new aiVector3D[numSubVerts];
|
||||
}
|
||||
|
||||
for( size_t a = 0; pMesh->HasTextureCoords( a) ; ++a ) {
|
||||
oMesh->mTextureCoords[a] = new aiVector3D[numSubVerts];
|
||||
oMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
|
||||
}
|
||||
|
||||
for( size_t a = 0; pMesh->HasVertexColors( a); ++a ) {
|
||||
oMesh->mColors[a] = new aiColor4D[numSubVerts];
|
||||
}
|
||||
|
||||
// and copy over the data, generating faces with linear indices along the way
|
||||
oMesh->mFaces = new aiFace[numSubFaces];
|
||||
|
||||
for(unsigned int a = 0; a < numSubFaces; ++a ) {
|
||||
|
||||
const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
|
||||
aiFace& dstFace = oMesh->mFaces[a];
|
||||
dstFace.mNumIndices = srcFace.mNumIndices;
|
||||
dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
|
||||
|
||||
// accumulate linearly all the vertices of the source face
|
||||
for( size_t b = 0; b < dstFace.mNumIndices; ++b ) {
|
||||
dstFace.mIndices[b] = vMap[srcFace.mIndices[b]];
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int srcIndex = 0; srcIndex < pMesh->mNumVertices; ++srcIndex ) {
|
||||
unsigned int nvi = vMap[srcIndex];
|
||||
if(nvi==UINT_MAX) {
|
||||
continue;
|
||||
}
|
||||
|
||||
oMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
|
||||
if( pMesh->HasNormals() ) {
|
||||
oMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
|
||||
}
|
||||
|
||||
if( pMesh->HasTangentsAndBitangents() ) {
|
||||
oMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
|
||||
oMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
|
||||
}
|
||||
for( size_t c = 0, cc = pMesh->GetNumUVChannels(); c < cc; ++c ) {
|
||||
oMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
|
||||
}
|
||||
for( size_t c = 0, cc = pMesh->GetNumColorChannels(); c < cc; ++c ) {
|
||||
oMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
|
||||
}
|
||||
}
|
||||
|
||||
if(~subFlags&AI_SUBMESH_FLAGS_SANS_BONES) {
|
||||
std::vector<unsigned int> subBones(pMesh->mNumBones,0);
|
||||
|
||||
for(unsigned int a=0;a<pMesh->mNumBones;++a) {
|
||||
const aiBone* bone = pMesh->mBones[a];
|
||||
|
||||
for(unsigned int b=0;b<bone->mNumWeights;b++) {
|
||||
unsigned int v = vMap[bone->mWeights[b].mVertexId];
|
||||
|
||||
if(v!=UINT_MAX) {
|
||||
subBones[a]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int a=0;a<pMesh->mNumBones;++a) {
|
||||
if(subBones[a]>0) {
|
||||
oMesh->mNumBones++;
|
||||
}
|
||||
}
|
||||
|
||||
if(oMesh->mNumBones) {
|
||||
oMesh->mBones = new aiBone*[oMesh->mNumBones]();
|
||||
unsigned int nbParanoia = oMesh->mNumBones;
|
||||
|
||||
oMesh->mNumBones = 0; //rewind
|
||||
|
||||
for(unsigned int a=0;a<pMesh->mNumBones;++a) {
|
||||
if(subBones[a]==0) {
|
||||
continue;
|
||||
}
|
||||
aiBone *newBone = new aiBone;
|
||||
oMesh->mBones[oMesh->mNumBones++] = newBone;
|
||||
|
||||
const aiBone* bone = pMesh->mBones[a];
|
||||
|
||||
newBone->mName = bone->mName;
|
||||
newBone->mOffsetMatrix = bone->mOffsetMatrix;
|
||||
newBone->mWeights = new aiVertexWeight[subBones[a]];
|
||||
|
||||
for(unsigned int b=0;b<bone->mNumWeights;b++) {
|
||||
const unsigned int v = vMap[bone->mWeights[b].mVertexId];
|
||||
|
||||
if(v!=UINT_MAX) {
|
||||
aiVertexWeight w(v,bone->mWeights[b].mWeight);
|
||||
newBone->mWeights[newBone->mNumWeights++] = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ai_assert(nbParanoia==oMesh->mNumBones);
|
||||
}
|
||||
}
|
||||
|
||||
return oMesh;
|
||||
}
|
||||
|
||||
} // namespace Assimp
|
|
@ -191,34 +191,6 @@ inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max)
|
|||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** @brief Extract single strings from a list of identifiers
|
||||
* @param in Input string list.
|
||||
* @param out Receives a list of clean output strings
|
||||
* @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST
|
||||
*/
|
||||
inline void ConvertListToStrings(const std::string& in, std::list<std::string>& out)
|
||||
{
|
||||
const char* s = in.c_str();
|
||||
while (*s) {
|
||||
SkipSpacesAndLineEnd(&s);
|
||||
if (*s == '\'') {
|
||||
const char* base = ++s;
|
||||
while (*s != '\'') {
|
||||
++s;
|
||||
if (*s == '\0') {
|
||||
DefaultLogger::get()->error("ConvertListToString: String list is ill-formatted");
|
||||
return;
|
||||
}
|
||||
}
|
||||
out.push_back(std::string(base,(size_t)(s-base)));
|
||||
++s;
|
||||
}
|
||||
else {
|
||||
out.push_back(GetNextToken(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** @brief Compute the newell normal of a polygon regardless of its shape
|
||||
|
@ -271,55 +243,37 @@ inline void NewellNormal (aiVector3D& out, int num, float* x, float* y, float* z
|
|||
out = aiVector3D(sum_yz,sum_zx,sum_xy);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// -------------------------------------------------------------------------------
|
||||
/** @brief Compute newell normal of a polgon regardless of its shape
|
||||
*
|
||||
* @param out Receives the output normal
|
||||
* @param data Input vertices
|
||||
* @param idx Index buffer
|
||||
* @param num Number of indices
|
||||
*/
|
||||
inline void NewellNormal (aiVector3D& out, const aiVector3D* data, unsigned int* idx, unsigned int num )
|
||||
{
|
||||
// TODO: intended to be used in GenNormals.
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** Little helper function to calculate the quadratic difference
|
||||
* of two colours.
|
||||
* @param pColor1 First color
|
||||
* @param pColor2 second color
|
||||
* @return Quadratic color difference
|
||||
*/
|
||||
* @return Quadratic color difference */
|
||||
inline float GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2)
|
||||
{
|
||||
const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g,
|
||||
pColor1.b - pColor2.b, pColor1.a - pColor2.a);
|
||||
|
||||
const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a);
|
||||
return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** @brief Extract single strings from a list of identifiers
|
||||
* @param in Input string list.
|
||||
* @param out Receives a list of clean output strings
|
||||
* @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST */
|
||||
void ConvertListToStrings(const std::string& in, std::list<std::string>& out);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** @brief Compute the AABB of a mesh after applying a given transform
|
||||
* @param mesh Input mesh
|
||||
* @param[out] min Receives minimum transformed vertex
|
||||
* @param[out] max Receives maximum transformed vertex
|
||||
* @param m Transformation matrix to be applied
|
||||
*/
|
||||
inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max,
|
||||
const aiMatrix4x4& m)
|
||||
{
|
||||
min = aiVector3D (10e10f, 10e10f, 10e10f);
|
||||
max = aiVector3D (-10e10f,-10e10f,-10e10f);
|
||||
for (unsigned int i = 0;i < mesh->mNumVertices;++i)
|
||||
{
|
||||
const aiVector3D v = m * mesh->mVertices[i];
|
||||
min = std::min(v,min);
|
||||
max = std::max(v,max);
|
||||
}
|
||||
}
|
||||
* @param m Transformation matrix to be applied */
|
||||
void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, const aiMatrix4x4& m);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
/** @brief Helper function to determine the 'real' center of a mesh
|
||||
|
@ -328,191 +282,65 @@ inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D
|
|||
* @param mesh Input mesh
|
||||
* @param[out] min Minimum vertex of the mesh
|
||||
* @param[out] max maximum vertex of the mesh
|
||||
* @param[out] out Center point
|
||||
*/
|
||||
inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
|
||||
{
|
||||
ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max);
|
||||
out = min + (max-min)*0.5f;
|
||||
}
|
||||
* @param[out] out Center point */
|
||||
void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Helper function to determine the 'real' center of a mesh after applying a given transform
|
||||
inline void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,
|
||||
aiVector3D& max, const aiMatrix4x4& m)
|
||||
{
|
||||
FindAABBTransformed(mesh,min,max,m);
|
||||
out = min + (max-min)*0.5f;
|
||||
}
|
||||
void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,aiVector3D& max, const aiMatrix4x4& m);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Helper function to determine the 'real' center of a mesh
|
||||
inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
|
||||
{
|
||||
aiVector3D min,max;
|
||||
FindMeshCenter(mesh,out,min,max);
|
||||
}
|
||||
void FindMeshCenter (aiMesh* mesh, aiVector3D& out);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Helper function to determine the 'real' center of a mesh after applying a given transform
|
||||
inline void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,
|
||||
const aiMatrix4x4& m)
|
||||
{
|
||||
aiVector3D min,max;
|
||||
FindMeshCenterTransformed(mesh,out,min,max,m);
|
||||
}
|
||||
void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,const aiMatrix4x4& m);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Compute a good epsilon value for position comparisons on a mesh
|
||||
inline float ComputePositionEpsilon(const aiMesh* pMesh)
|
||||
{
|
||||
const float epsilon = 1e-4f;
|
||||
float ComputePositionEpsilon(const aiMesh* pMesh);
|
||||
|
||||
// calculate the position bounds so we have a reliable epsilon to check position differences against
|
||||
aiVector3D minVec, maxVec;
|
||||
ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec);
|
||||
return (maxVec - minVec).Length() * epsilon;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Compute a good epsilon value for position comparisons on a array of meshes
|
||||
inline float ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num)
|
||||
{
|
||||
const float epsilon = 1e-4f;
|
||||
float ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num);
|
||||
|
||||
// calculate the position bounds so we have a reliable epsilon to check position differences against
|
||||
aiVector3D minVec, maxVec, mi, ma;
|
||||
MinMaxChooser<aiVector3D>()(minVec,maxVec);
|
||||
|
||||
for (size_t a = 0; a < num; ++a) {
|
||||
const aiMesh* pMesh = pMeshes[a];
|
||||
ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma);
|
||||
|
||||
minVec = std::min(minVec,mi);
|
||||
maxVec = std::max(maxVec,ma);
|
||||
}
|
||||
return (maxVec - minVec).Length() * epsilon;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Compute an unique value for the vertex format of a mesh
|
||||
inline unsigned int GetMeshVFormatUnique(aiMesh* pcMesh)
|
||||
{
|
||||
ai_assert(NULL != pcMesh);
|
||||
unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh);
|
||||
|
||||
// FIX: the hash may never be 0. Otherwise a comparison against
|
||||
// nullptr could be successful
|
||||
unsigned int iRet = 1;
|
||||
|
||||
// normals
|
||||
if (pcMesh->HasNormals())iRet |= 0x2;
|
||||
// tangents and bitangents
|
||||
if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4;
|
||||
|
||||
#ifdef BOOST_STATIC_ASSERT
|
||||
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
|
||||
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
|
||||
#endif
|
||||
|
||||
// texture coordinates
|
||||
unsigned int p = 0;
|
||||
while (pcMesh->HasTextureCoords(p))
|
||||
{
|
||||
iRet |= (0x100 << p);
|
||||
if (3 == pcMesh->mNumUVComponents[p])
|
||||
iRet |= (0x10000 << p);
|
||||
|
||||
++p;
|
||||
}
|
||||
// vertex colors
|
||||
p = 0;
|
||||
while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++);
|
||||
return iRet;
|
||||
}
|
||||
|
||||
// defs for ComputeVertexBoneWeightTable()
|
||||
typedef std::pair <unsigned int,float> PerVertexWeight;
|
||||
typedef std::vector <PerVertexWeight> VertexWeightTable;
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Compute a per-vertex bone weight table
|
||||
// please .... delete result with operator delete[] ...
|
||||
inline VertexWeightTable* ComputeVertexBoneWeightTable(aiMesh* pMesh)
|
||||
{
|
||||
if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones)
|
||||
return NULL;
|
||||
VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh);
|
||||
|
||||
VertexWeightTable* avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
|
||||
for (unsigned int i = 0; i < pMesh->mNumBones;++i)
|
||||
{
|
||||
aiBone* bone = pMesh->mBones[i];
|
||||
for (unsigned int a = 0; a < bone->mNumWeights;++a) {
|
||||
const aiVertexWeight& weight = bone->mWeights[a];
|
||||
avPerVertexWeights[weight.mVertexId].push_back(
|
||||
std::pair<unsigned int,float>(i,weight.mWeight));
|
||||
}
|
||||
}
|
||||
return avPerVertexWeights;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Get a string for a given aiTextureType
|
||||
inline const char* TextureTypeToString(aiTextureType in)
|
||||
{
|
||||
switch (in)
|
||||
{
|
||||
case aiTextureType_NONE:
|
||||
return "n/a";
|
||||
case aiTextureType_DIFFUSE:
|
||||
return "Diffuse";
|
||||
case aiTextureType_SPECULAR:
|
||||
return "Specular";
|
||||
case aiTextureType_AMBIENT:
|
||||
return "Ambient";
|
||||
case aiTextureType_EMISSIVE:
|
||||
return "Emissive";
|
||||
case aiTextureType_OPACITY:
|
||||
return "Opacity";
|
||||
case aiTextureType_NORMALS:
|
||||
return "Normals";
|
||||
case aiTextureType_HEIGHT:
|
||||
return "Height";
|
||||
case aiTextureType_SHININESS:
|
||||
return "Shininess";
|
||||
case aiTextureType_DISPLACEMENT:
|
||||
return "Displacement";
|
||||
case aiTextureType_LIGHTMAP:
|
||||
return "Lightmap";
|
||||
case aiTextureType_REFLECTION:
|
||||
return "Reflection";
|
||||
case aiTextureType_UNKNOWN:
|
||||
return "Unknown";
|
||||
default:
|
||||
return "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";
|
||||
}
|
||||
}
|
||||
const char* TextureTypeToString(aiTextureType in);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Get a string for a given aiTextureMapping
|
||||
inline const char* MappingTypeToString(aiTextureMapping in)
|
||||
{
|
||||
switch (in)
|
||||
{
|
||||
case aiTextureMapping_UV:
|
||||
return "UV";
|
||||
case aiTextureMapping_BOX:
|
||||
return "Box";
|
||||
case aiTextureMapping_SPHERE:
|
||||
return "Sphere";
|
||||
case aiTextureMapping_CYLINDER:
|
||||
return "Cylinder";
|
||||
case aiTextureMapping_PLANE:
|
||||
return "Plane";
|
||||
case aiTextureMapping_OTHER:
|
||||
return "Other";
|
||||
default:
|
||||
return "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";
|
||||
}
|
||||
}
|
||||
const char* MappingTypeToString(aiTextureMapping in);
|
||||
|
||||
|
||||
// flags for MakeSubmesh()
|
||||
#define AI_SUBMESH_FLAGS_SANS_BONES 0x1
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Split a mesh given a list of faces to be contained in the sub mesh
|
||||
aiMesh* MakeSubmesh(const aiMesh *superMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags);
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Utility postprocess step to share the spatial sort tree between
|
||||
|
@ -560,5 +388,7 @@ class DestroySpatialSortProcess : public BaseProcess
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // ! namespace Assimp
|
||||
#endif // !! AI_PROCESS_HELPER_H_INCLUDED
|
||||
|
|
|
@ -288,6 +288,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
# define AI_LMW_MAX_WEIGHTS 0x4
|
||||
#endif // !! AI_LMW_MAX_WEIGHTS
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
/** @brief Set the deboning threshold higher to remove more bones
|
||||
*
|
||||
* This is used by the #aiProcess_Debone PostProcess-Step.
|
||||
* @note The default value is AI_DEBONE_THRESHOLD
|
||||
* Property type: float.*/
|
||||
#define AI_CONFIG_PP_DB_THRESHOLD \
|
||||
"PP_DB_THRESHOLD"
|
||||
|
||||
// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS
|
||||
#if (!defined AI_DEBONE_THRESHOLD)
|
||||
# define AI_DEBONE_THRESHOLD 1.0f
|
||||
#endif // !! AI_DEBONE_THRESHOLD
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
/** @brief Require all bones qualify for deboning before removing any
|
||||
*
|
||||
* This is used by the #aiProcess_Debone PostProcess-Step.
|
||||
* @note The default value is 0
|
||||
* Property type: bool.*/
|
||||
#define AI_CONFIG_PP_DB_ALL_OR_NONE \
|
||||
"PP_DB_ALL_OR_NONE"
|
||||
|
||||
/** @brief Default value for the #AI_CONFIG_PP_ICL_PTCACHE_SIZE property
|
||||
*/
|
||||
#ifndef PP_ICL_PTCACHE_SIZE
|
||||
|
|
|
@ -500,7 +500,21 @@ enum aiPostProcessSteps
|
|||
/** <hr>This step splits meshes with many bones into submeshes so that each
|
||||
* submesh has fewer or as many bones as a given limit.
|
||||
*/
|
||||
aiProcess_SplitByBoneCount = 0x2000000
|
||||
aiProcess_SplitByBoneCount = 0x2000000,
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
/** <hr>This step removes bones losslessly or according to some threshold.
|
||||
* In some cases (i.e. format that require it) exporters are forced to
|
||||
* assign dummy bone weights to otherwise static meshes assigned to
|
||||
* animated meshes. Since full, weight-based skinning is expensive but
|
||||
* animating nodes is extremely cheap, this step is offered to cleanup
|
||||
* the data in that regard.
|
||||
*
|
||||
* Use <tt>#AI_CONFIG_PP_DB_THRESHOLD</tt> to control this.
|
||||
* Use <tt>#AI_CONFIG_PP_DB_ALL_OR_NONE</tt> if you want bones removed if and
|
||||
* only if all bones within the scene qualify for removal.
|
||||
*/
|
||||
aiProcess_Debone = 0x4000000
|
||||
|
||||
// aiProcess_GenEntityMeshes = 0x100000,
|
||||
// aiProcess_OptimizeAnimations = 0x200000
|
||||
|
@ -600,6 +614,7 @@ enum aiPostProcessSteps
|
|||
aiProcess_FindInstances | \
|
||||
aiProcess_ValidateDataStructure | \
|
||||
aiProcess_OptimizeMeshes | \
|
||||
aiProcess_Debone | \
|
||||
0 )
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue