#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
#include "ColladaLoader.h"
#include <assimp/anim.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <assimp/importerdesc.h>
#include "ColladaParser.h"
#include "fast_atof.h"
#include "ParsingUtils.h"
#include "SkeletonMeshBuilder.h"
#include "CreateAnimMesh.h"
#include "time.h"
#include "math.h"
#include <algorithm>
#include <numeric>
#include <assimp/Defines.h>
using namespace Assimp;
using namespace Assimp::Formatter;
static const aiImporterDesc desc = {
"Collada Importer",
"",
"",
"http://collada.org",
aiImporterFlags_SupportTextFlavour,
1,
3,
1,
5,
"dae"
};
ColladaLoader::ColladaLoader()
: mFileName()
, mMeshIndexByID()
, mMaterialIndexByName()
, mMeshes()
, newMats()
, mCameras()
, mLights()
, mTextures()
, mAnims()
, noSkeletonMesh( false )
, ignoreUpDirection(false)
, mNodeNameCounter( 0 )
{}
ColladaLoader::~ColladaLoader()
{}
bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
std::string extension = GetExtension(pFile);
if( extension == "dae")
return true;
if( extension == "xml" || !extension.length() || checkSig) {
if (!pIOHandler)return true;
const char* tokens[] = {"collada"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
void ColladaLoader::SetupProperties(const Importer* pImp)
{
noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION,0) != 0;
}
const aiImporterDesc* ColladaLoader::GetInfo () const
{
return &desc;
}
void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
mFileName = pFile;
mMeshIndexByID.clear();
mMaterialIndexByName.clear();
mMeshes.clear();
mTargetMeshes.clear();
newMats.clear();
mLights.clear();
mCameras.clear();
mTextures.clear();
mAnims.clear();
ColladaParser parser( pIOHandler, pFile);
if( !parser.mRootNode)
throw DeadlyImportError( "Collada: File came out empty. Something is wrong here.");
newMats.reserve(parser.mMaterialLibrary.size()*2);
mMeshes.reserve(parser.mMeshLibrary.size()*2);
mCameras.reserve(parser.mCameraLibrary.size());
mLights.reserve(parser.mLightLibrary.size());
BuildMaterials( parser, pScene);
pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode);
FillMaterials(parser, pScene);
pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0,
0, parser.mUnitSize, 0, 0,
0, 0, parser.mUnitSize, 0,
0, 0, 0, 1);
if( !ignoreUpDirection ) {
if( parser.mUpDirection == ColladaParser::UP_X)
pScene->mRootNode->mTransformation *= aiMatrix4x4(
0, -1, 0, 0,
1, 0, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
else if( parser.mUpDirection == ColladaParser::UP_Z)
pScene->mRootNode->mTransformation *= aiMatrix4x4(
1, 0, 0, 0,
0, 0, 1, 0,
0, -1, 0, 0,
0, 0, 0, 1);
}
StoreSceneMeshes( pScene);
StoreSceneMaterials( pScene);
StoreSceneLights( pScene);
StoreSceneCameras( pScene);
StoreAnimations( pScene, parser);
if (!pScene->mNumMeshes) {
if (!noSkeletonMesh) {
SkeletonMeshBuilder hero(pScene);
}
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
}
}
aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode)
{
aiNode* node = new aiNode();
node->mName.Set( FindNameForNode( pNode));
node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms);
std::vector<const Collada::Node*> instances;
ResolveNodeInstances(pParser,pNode,instances);
node->mNumChildren = static_cast<unsigned int>(pNode->mChildren.size()+instances.size());
node->mChildren = new aiNode*[node->mNumChildren];
for( size_t a = 0; a < pNode->mChildren.size(); a++)
{
node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]);
node->mChildren[a]->mParent = node;
}
for( size_t a = 0; a < instances.size(); a++)
{
node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy( pParser, instances[a]);
node->mChildren[pNode->mChildren.size() + a]->mParent = node;
}
BuildMeshesForNode( pParser, pNode, node);
BuildCamerasForNode(pParser, pNode, node);
BuildLightsForNode(pParser, pNode, node);
return node;
}
void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
std::vector<const Collada::Node*>& resolved)
{
resolved.reserve(pNode->mNodeInstances.size());
for (const auto &nodeInst: pNode->mNodeInstances)
{
const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find(nodeInst.mNode);
const Collada::Node* nd = itt == pParser.mNodeLibrary.end() ? NULL : (*itt).second;
if (!nd) {
nd = FindNode(pParser.mRootNode, nodeInst.mNode);
}
if (!nd)
DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + nodeInst.mNode);
else {
resolved.push_back(nd);
}
}
}
void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
const Collada::SemanticMappingTable& table)
{
std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel);
if (it != table.mMap.end()) {
if (it->second.mType != Collada::IT_Texcoord)
DefaultLogger::get()->error("Collada: Unexpected effect input mapping");
sampler.mUVId = it->second.mSet;
}
}
void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
{
for( const Collada::LightInstance& lid : pNode->mLights)
{
ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight);
if( srcLightIt == pParser.mLightLibrary.end())
{
DefaultLogger::get()->warn("Collada: Unable to find light for ID \"" + lid.mLight + "\". Skipping.");
continue;
}
const Collada::Light* srcLight = &srcLightIt->second;
aiLight* out = new aiLight();
out->mName = pTarget->mName;
out->mType = (aiLightSourceType)srcLight->mType;
out->mDirection = aiVector3D(0.f,0.f,-1.f);
out->mAttenuationConstant = srcLight->mAttConstant;
out->mAttenuationLinear = srcLight->mAttLinear;
out->mAttenuationQuadratic = srcLight->mAttQuadratic;
out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor*srcLight->mIntensity;
if (out->mType == aiLightSource_AMBIENT) {
out->mColorDiffuse = out->mColorSpecular = aiColor3D(0, 0, 0);
out->mColorAmbient = srcLight->mColor*srcLight->mIntensity;
}
else {
out->mColorDiffuse = out->mColorSpecular = srcLight->mColor*srcLight->mIntensity;
out->mColorAmbient = aiColor3D(0, 0, 0);
}
if (out->mType == aiLightSource_SPOT) {
out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle );
if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
{
if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
{
out->mAngleOuterCone = std::acos(std::pow(0.1f,1.f/srcLight->mFalloffExponent))+
out->mAngleInnerCone;
}
else {
out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle );
if (out->mAngleOuterCone < out->mAngleInnerCone)
std::swap(out->mAngleInnerCone,out->mAngleOuterCone);
}
}
else out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle );
}
mLights.push_back(out);
}
}
void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
{
for( const Collada::CameraInstance& cid : pNode->mCameras)
{
ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera);
if( srcCameraIt == pParser.mCameraLibrary.end())
{
DefaultLogger::get()->warn("Collada: Unable to find camera for ID \"" + cid.mCamera + "\". Skipping.");
continue;
}
const Collada::Camera* srcCamera = &srcCameraIt->second;
if (srcCamera->mOrtho) {
DefaultLogger::get()->warn("Collada: Orthographic cameras are not supported.");
}
aiCamera* out = new aiCamera();
out->mName = pTarget->mName;
out->mLookAt = aiVector3D(0.f,0.f,-1.f);
out->mClipPlaneFar = srcCamera->mZFar;
out->mClipPlaneNear = srcCamera->mZNear;
if (srcCamera->mAspect != 10e10f)
out->mAspect = srcCamera->mAspect;
if (srcCamera->mHorFov != 10e10f) {
out->mHorizontalFOV = srcCamera->mHorFov;
if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) {
out->mAspect = std::tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) /
std::tan(AI_DEG_TO_RAD(srcCamera->mVerFov));
}
}
else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) {
out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(std::atan(srcCamera->mAspect *
std::tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f)));
}
out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV);
mCameras.push_back(out);
}
}
void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
{
std::vector<size_t> newMeshRefs;
newMeshRefs.reserve(pNode->mMeshes.size());
for( const Collada::MeshInstance& mid : pNode->mMeshes)
{
const Collada::Mesh* srcMesh = NULL;
const Collada::Controller* srcController = NULL;
ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController);
if( srcMeshIt == pParser.mMeshLibrary.end())
{
ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController);
if( srcContrIt != pParser.mControllerLibrary.end())
{
srcController = &srcContrIt->second;
srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId);
if( srcMeshIt != pParser.mMeshLibrary.end())
srcMesh = srcMeshIt->second;
}
if( !srcMesh)
{
DefaultLogger::get()->warn( format() << "Collada: Unable to find geometry for ID \"" << mid.mMeshOrController << "\". Skipping." );
continue;
}
} else
{
srcMesh = srcMeshIt->second;
}
size_t vertexStart = 0, faceStart = 0;
for( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm)
{
const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm];
if( submesh.mNumFaces == 0)
continue;
std::string meshMaterial;
std::map<std::string, Collada::SemanticMappingTable >::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial);
const Collada::SemanticMappingTable* table = NULL;
if( meshMatIt != mid.mMaterials.end())
{
table = &meshMatIt->second;
meshMaterial = table->mMatName;
}
else
{
DefaultLogger::get()->warn( format() << "Collada: No material specified for subgroup <" << submesh.mMaterial << "> in geometry <" << mid.mMeshOrController << ">." );
if( !mid.mMaterials.empty() )
meshMaterial = mid.mMaterials.begin()->second.mMatName;
}
std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find( meshMaterial);
unsigned int matIdx;
if( matIt != mMaterialIndexByName.end())
matIdx = static_cast<unsigned int>(matIt->second);
else
matIdx = 0;
if (table && !table->mMap.empty() ) {
std::pair<Collada::Effect*, aiMaterial*>& mat = newMats[matIdx];
ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table);
ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table);
}
ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial);
std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find( index);
if( dstMeshIt != mMeshIndexByID.end()) {
newMeshRefs.push_back( dstMeshIt->second);
}
else
{
aiMesh* dstMesh = CreateMesh( pParser, srcMesh, submesh, srcController, vertexStart, faceStart);
newMeshRefs.push_back( mMeshes.size());
mMeshIndexByID[index] = mMeshes.size();
mMeshes.push_back( dstMesh);
vertexStart += dstMesh->mNumVertices; faceStart += submesh.mNumFaces;
dstMesh->mMaterialIndex = matIdx;
if(dstMesh->mName.length == 0)
{
dstMesh->mName = mid.mMeshOrController;
}
}
}
}
pTarget->mNumMeshes = static_cast<unsigned int>(newMeshRefs.size());
if( newMeshRefs.size())
{
struct UIntTypeConverter
{
unsigned int operator()(const size_t& v) const
{
return static_cast<unsigned int>(v);
}
};
pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes];
std::transform( newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes, UIntTypeConverter());
}
}
aiMesh *ColladaLoader::findMesh(std::string meshid)
{
for (unsigned int i = 0; i < mMeshes.size(); i++)
if (std::string(mMeshes[i]->mName.data) == meshid)
return mMeshes[i];
for (unsigned int i = 0; i < mTargetMeshes.size(); i++)
if (std::string(mTargetMeshes[i]->mName.data) == meshid)
return mTargetMeshes[i];
return NULL;
}
aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace)
{
aiMesh* dstMesh = new aiMesh;
dstMesh->mName = pSrcMesh->mName;
const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace,
pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, size_t(0));
dstMesh->mNumVertices = static_cast<unsigned int>(numVertices);
dstMesh->mVertices = new aiVector3D[numVertices];
std::copy( pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() +
pStartVertex + numVertices, dstMesh->mVertices);
if( pSrcMesh->mNormals.size() >= pStartVertex + numVertices)
{
dstMesh->mNormals = new aiVector3D[numVertices];
std::copy( pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() +
pStartVertex + numVertices, dstMesh->mNormals);
}
if( pSrcMesh->mTangents.size() >= pStartVertex + numVertices)
{
dstMesh->mTangents = new aiVector3D[numVertices];
std::copy( pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() +
pStartVertex + numVertices, dstMesh->mTangents);
}
if( pSrcMesh->mBitangents.size() >= pStartVertex + numVertices)
{
dstMesh->mBitangents = new aiVector3D[numVertices];
std::copy( pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() +
pStartVertex + numVertices, dstMesh->mBitangents);
}
for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
{
if( pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices)
{
dstMesh->mTextureCoords[real] = new aiVector3D[numVertices];
for( size_t b = 0; b < numVertices; ++b)
dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b];
dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a];
++real;
}
}
for( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
{
if( pSrcMesh->mColors[a].size() >= pStartVertex + numVertices)
{
dstMesh->mColors[real] = new aiColor4D[numVertices];
std::copy( pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices,dstMesh->mColors[real]);
++real;
}
}
size_t vertex = 0;
dstMesh->mNumFaces = static_cast<unsigned int>(pSubMesh.mNumFaces);
dstMesh->mFaces = new aiFace[dstMesh->mNumFaces];
for( size_t a = 0; a < dstMesh->mNumFaces; ++a)
{
size_t s = pSrcMesh->mFaceSize[ pStartFace + a];
aiFace& face = dstMesh->mFaces[a];
face.mNumIndices = static_cast<unsigned int>(s);
face.mIndices = new unsigned int[s];
for( size_t b = 0; b < s; ++b)
face.mIndices[b] = static_cast<unsigned int>(vertex++);
}
std::vector<aiMesh*> targetMeshes;
std::vector<float> targetWeights;
Collada::MorphMethod method;
for(std::map<std::string, Collada::Controller>::const_iterator it = pParser.mControllerLibrary.begin();
it != pParser.mControllerLibrary.end(); it++)
{
const Collada::Controller &c = it->second;
const Collada::Mesh* baseMesh = pParser.ResolveLibraryReference( pParser.mMeshLibrary, c.mMeshId);
if (c.mType == Collada::Morph && baseMesh->mName == pSrcMesh->mName)
{
const Collada::Accessor& targetAccessor = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, c.mMorphTarget);
const Collada::Accessor& weightAccessor = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, c.mMorphWeight);
const Collada::Data& targetData = pParser.ResolveLibraryReference( pParser.mDataLibrary, targetAccessor.mSource);
const Collada::Data& weightData = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightAccessor.mSource);
method = c.mMethod;
if (!targetData.mIsStringArray)
throw DeadlyImportError( "target data must contain id. ");
if (weightData.mIsStringArray)
throw DeadlyImportError( "target weight data must not be textual ");
for (unsigned int i = 0; i < targetData.mStrings.size(); ++i)
{
const Collada::Mesh* targetMesh = pParser.ResolveLibraryReference(pParser.mMeshLibrary, targetData.mStrings.at(i));
aiMesh *aimesh = findMesh(targetMesh->mName);
if (!aimesh)
{
if (targetMesh->mSubMeshes.size() > 1)
throw DeadlyImportError( "Morhing target mesh must be a single");
aimesh = CreateMesh(pParser, targetMesh, targetMesh->mSubMeshes.at(0), NULL, 0, 0);
mTargetMeshes.push_back(aimesh);
}
targetMeshes.push_back(aimesh);
}
for (unsigned int i = 0; i < weightData.mValues.size(); ++i)
targetWeights.push_back(weightData.mValues.at(i));
}
}
if (targetMeshes.size() > 0 && targetWeights.size() == targetMeshes.size())
{
std::vector<aiAnimMesh*> animMeshes;
for (unsigned int i = 0; i < targetMeshes.size(); i++)
{
aiAnimMesh *animMesh = aiCreateAnimMesh(targetMeshes.at(i));
animMesh->mWeight = targetWeights[i];
animMeshes.push_back(animMesh);
}
dstMesh->mMethod = (method == Collada::Relative)
? aiMorphingMethod_MORPH_RELATIVE
: aiMorphingMethod_MORPH_NORMALIZED;
dstMesh->mAnimMeshes = new aiAnimMesh*[animMeshes.size()];
dstMesh->mNumAnimMeshes = animMeshes.size();
for (unsigned int i = 0; i < animMeshes.size(); i++)
dstMesh->mAnimMeshes[i] = animMeshes.at(i);
}
if( pSrcController && pSrcController->mType == Collada::Skin)
{
const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource);
const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource);
const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource);
const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource);
const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor);
if( &weightNamesAcc != &jointNamesAcc)
throw DeadlyImportError( "Temporary implementational laziness. If you read this, please report to the author.");
const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor);
const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource);
if( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray)
throw DeadlyImportError( "Data type mismatch while resolving mesh joints");
if( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1)
throw DeadlyImportError( "Unsupported vertex_weight addressing scheme. ");
size_t numBones = jointNames.mStrings.size();
std::vector<std::vector<aiVertexWeight> > dstBones( numBones);
typedef std::vector< std::pair<size_t, size_t> > IndexPairVector;
std::vector<IndexPairVector::const_iterator> weightStartPerVertex;
weightStartPerVertex.resize(pSrcController->mWeightCounts.size(),pSrcController->mWeights.end());
IndexPairVector::const_iterator pit = pSrcController->mWeights.begin();
for( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a)
{
weightStartPerVertex[a] = pit;
pit += pSrcController->mWeightCounts[a];
}
for( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a)
{
size_t orgIndex = pSrcMesh->mFacePosIndices[a];
IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex];
size_t pairCount = pSrcController->mWeightCounts[orgIndex];
for( size_t b = 0; b < pairCount; ++b, ++iit)
{
size_t jointIndex = iit->first;
size_t vertexIndex = iit->second;
ai_real weight = ReadFloat( weightsAcc, weights, vertexIndex, 0);
if( weight > 0.0f)
{
aiVertexWeight w;
w.mVertexId = static_cast<unsigned int>(a - pStartVertex);
w.mWeight = weight;
dstBones[jointIndex].push_back( w);
}
}
}
size_t numRemainingBones = 0;
for( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it)
if( it->size() > 0)
numRemainingBones++;
dstMesh->mNumBones = static_cast<unsigned int>(numRemainingBones);
dstMesh->mBones = new aiBone*[numRemainingBones];
size_t boneCount = 0;
for( size_t a = 0; a < numBones; ++a)
{
if( dstBones[a].size() == 0)
continue;
aiBone* bone = new aiBone;
bone->mName = ReadString( jointNamesAcc, jointNames, a);
bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0);
bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1);
bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2);
bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3);
bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4);
bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5);
bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6);
bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7);
bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8);
bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9);
bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10);
bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11);
bone->mNumWeights = static_cast<unsigned int>(dstBones[a].size());
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights);
aiMatrix4x4 bindShapeMatrix;
bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0];
bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1];
bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2];
bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3];
bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4];
bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5];
bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6];
bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7];
bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8];
bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9];
bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10];
bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11];
bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12];
bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13];
bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14];
bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15];
bone->mOffsetMatrix *= bindShapeMatrix;
const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data);
if( !bnode)
bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data);
if( bnode)
bone->mName.Set( FindNameForNode( bnode));
else
DefaultLogger::get()->warn( format() << "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"" << bone->mName.data << "\"." );
dstMesh->mBones[boneCount++] = bone;
}
}
return dstMesh;
}
void ColladaLoader::StoreSceneMeshes( aiScene* pScene)
{
pScene->mNumMeshes = static_cast<unsigned int>(mMeshes.size());
if( mMeshes.size() > 0)
{
pScene->mMeshes = new aiMesh*[mMeshes.size()];
std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes);
mMeshes.clear();
}
}
void ColladaLoader::StoreSceneCameras( aiScene* pScene)
{
pScene->mNumCameras = static_cast<unsigned int>(mCameras.size());
if( mCameras.size() > 0)
{
pScene->mCameras = new aiCamera*[mCameras.size()];
std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras);
mCameras.clear();
}
}
void ColladaLoader::StoreSceneLights( aiScene* pScene)
{
pScene->mNumLights = static_cast<unsigned int>(mLights.size());
if( mLights.size() > 0)
{
pScene->mLights = new aiLight*[mLights.size()];
std::copy( mLights.begin(), mLights.end(), pScene->mLights);
mLights.clear();
}
}
void ColladaLoader::StoreSceneTextures( aiScene* pScene)
{
pScene->mNumTextures = static_cast<unsigned int>(mTextures.size());
if( mTextures.size() > 0)
{
pScene->mTextures = new aiTexture*[mTextures.size()];
std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures);
mTextures.clear();
}
}
void ColladaLoader::StoreSceneMaterials( aiScene* pScene)
{
pScene->mNumMaterials = static_cast<unsigned int>(newMats.size());
if (newMats.size() > 0) {
pScene->mMaterials = new aiMaterial*[newMats.size()];
for (unsigned int i = 0; i < newMats.size();++i)
pScene->mMaterials[i] = newMats[i].second;
newMats.clear();
}
}
void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser)
{
StoreAnimations( pScene, pParser, &pParser.mAnims, "");
for( size_t a = 0; a < mAnims.size(); ++a)
{
aiAnimation* templateAnim = mAnims[a];
if( templateAnim->mNumChannels == 1)
{
std::vector<size_t> collectedAnimIndices;
for( size_t b = a+1; b < mAnims.size(); ++b)
{
aiAnimation* other = mAnims[b];
if( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && other->mTicksPerSecond == templateAnim->mTicksPerSecond )
collectedAnimIndices.push_back( b);
}
if( !collectedAnimIndices.empty() )
{
aiAnimation* combinedAnim = new aiAnimation();
combinedAnim->mName = aiString( std::string( "combinedAnim_") + char( '0' + a));
combinedAnim->mDuration = templateAnim->mDuration;
combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond;
combinedAnim->mNumChannels = static_cast<unsigned int>(collectedAnimIndices.size() + 1);
combinedAnim->mChannels = new aiNodeAnim*[combinedAnim->mNumChannels];
combinedAnim->mChannels[0] = templateAnim->mChannels[0];
templateAnim->mChannels[0] = NULL;
delete templateAnim;
mAnims[a] = combinedAnim;
for( size_t b = 0; b < collectedAnimIndices.size(); ++b)
{
aiAnimation* srcAnimation = mAnims[collectedAnimIndices[b]];
combinedAnim->mChannels[1 + b] = srcAnimation->mChannels[0];
srcAnimation->mChannels[0] = NULL;
delete srcAnimation;
}
while( !collectedAnimIndices.empty() )
{
mAnims.erase( mAnims.begin() + collectedAnimIndices.back());
collectedAnimIndices.pop_back();
}
}
}
}
if( !mAnims.empty())
{
pScene->mNumAnimations = static_cast<unsigned int>(mAnims.size());
pScene->mAnimations = new aiAnimation*[mAnims.size()];
std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations);
}
mAnims.clear();
}
void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string &pPrefix)
{
std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName;
for( std::vector<Collada::Animation*>::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it)
StoreAnimations( pScene, pParser, *it, animName);
if( !pSrcAnim->mChannels.empty())
CreateAnimation( pScene, pParser, pSrcAnim, animName);
}
struct MorphTimeValues
{
float mTime;
struct key
{
float mWeight;
unsigned int mValue;
};
std::vector<key> mKeys;
};
void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, float weight, unsigned int value)
{
MorphTimeValues::key k;
k.mValue = value;
k.mWeight = weight;
if (values.size() == 0 || time < values[0].mTime)
{
MorphTimeValues val;
val.mTime = time;
val.mKeys.push_back(k);
values.insert(values.begin(), val);
return;
}
if (time > values.back().mTime)
{
MorphTimeValues val;
val.mTime = time;
val.mKeys.push_back(k);
values.insert(values.end(), val);
return;
}
for (unsigned int i = 0; i < values.size(); i++)
{
if (std::abs(time - values[i].mTime) < 1e-6f)
{
values[i].mKeys.push_back(k);
return;
} else if (time > values[i].mTime && time < values[i+1].mTime)
{
MorphTimeValues val;
val.mTime = time;
val.mKeys.push_back(k);
values.insert(values.begin() + i, val);
return;
}
}
}
float getWeightAtKey(const std::vector<MorphTimeValues> &values, int key, unsigned int value)
{
for (unsigned int i = 0; i < values[key].mKeys.size(); i++)
{
if (values[key].mKeys[i].mValue == value)
return values[key].mKeys[i].mWeight;
}
return 0.0f;
}
void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName)
{
std::vector<const aiNode*> nodes;
CollectNodes( pScene->mRootNode, nodes);
std::vector<aiNodeAnim*> anims;
std::vector<aiMeshMorphAnim*> morphAnims;
for( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
{
std::vector<Collada::ChannelEntry> entries;
std::string nodeName = (*nit)->mName.data;
const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName);
if( !srcNode)
continue;
for( std::vector<Collada::AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin();
cit != pSrcAnim->mChannels.end(); ++cit)
{
const Collada::AnimationChannel& srcChannel = *cit;
Collada::ChannelEntry entry;
std::string::size_type slashPos = srcChannel.mTarget.find( '/');
if( slashPos == std::string::npos)
{
std::string::size_type targetPos = srcChannel.mTarget.find(srcNode->mID);
if (targetPos == std::string::npos)
continue;
entry.mChannel = &(*cit);
entry.mTargetId = srcChannel.mTarget.substr(targetPos + pSrcAnim->mName.length(),
srcChannel.mTarget.length() - targetPos - pSrcAnim->mName.length());
if (entry.mTargetId.front() == '-')
entry.mTargetId = entry.mTargetId.substr(1);
entries.push_back(entry);
continue;
}
if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos)
continue;
std::string targetID = srcChannel.mTarget.substr( 0, slashPos);
if( targetID != srcNode->mID)
continue;
std::string::size_type dotPos = srcChannel.mTarget.find( '.');
if( dotPos != std::string::npos)
{
if( srcChannel.mTarget.find( '.', dotPos+1) != std::string::npos)
continue;
entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1);
std::string subElement = srcChannel.mTarget.substr( dotPos+1);
if( subElement == "ANGLE")
entry.mSubElement = 3; else if( subElement == "X")
entry.mSubElement = 0;
else if( subElement == "Y")
entry.mSubElement = 1;
else if( subElement == "Z")
entry.mSubElement = 2;
else
DefaultLogger::get()->warn( format() << "Unknown anim subelement <" << subElement << ">. Ignoring" );
} else
{
entry.mTransformId = srcChannel.mTarget.substr( slashPos+1);
}
std::string::size_type bracketPos = srcChannel.mTarget.find('(');
if (bracketPos != std::string::npos)
{
entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, bracketPos - slashPos - 1);
std::string subElement = srcChannel.mTarget.substr(bracketPos);
if (subElement == "(0)(0)")
entry.mSubElement = 0;
else if (subElement == "(1)(0)")
entry.mSubElement = 1;
else if (subElement == "(2)(0)")
entry.mSubElement = 2;
else if (subElement == "(3)(0)")
entry.mSubElement = 3;
else if (subElement == "(0)(1)")
entry.mSubElement = 4;
else if (subElement == "(1)(1)")
entry.mSubElement = 5;
else if (subElement == "(2)(1)")
entry.mSubElement = 6;
else if (subElement == "(3)(1)")
entry.mSubElement = 7;
else if (subElement == "(0)(2)")
entry.mSubElement = 8;
else if (subElement == "(1)(2)")
entry.mSubElement = 9;
else if (subElement == "(2)(2)")
entry.mSubElement = 10;
else if (subElement == "(3)(2)")
entry.mSubElement = 11;
else if (subElement == "(0)(3)")
entry.mSubElement = 12;
else if (subElement == "(1)(3)")
entry.mSubElement = 13;
else if (subElement == "(2)(3)")
entry.mSubElement = 14;
else if (subElement == "(3)(3)")
entry.mSubElement = 15;
}
entry.mTransformIndex = SIZE_MAX;
for( size_t a = 0; a < srcNode->mTransforms.size(); ++a)
if( srcNode->mTransforms[a].mID == entry.mTransformId)
entry.mTransformIndex = a;
if( entry.mTransformIndex == SIZE_MAX)
{
if (entry.mTransformId.find("morph-weights") != std::string::npos)
{
entry.mTargetId = entry.mTransformId;
entry.mTransformId = "";
} else
continue;
}
entry.mChannel = &(*cit);
entries.push_back( entry);
}
if( entries.empty())
continue;
ai_real startTime = ai_real( 1e20 ), endTime = ai_real( -1e20 );
for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
{
Collada::ChannelEntry& e = *it;
e.mTimeAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceTimes);
e.mTimeData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mTimeAccessor->mSource);
e.mValueAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceValues);
e.mValueData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mValueAccessor->mSource);
if( e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
throw DeadlyImportError( format() << "Time count / value count mismatch in animation channel \"" << e.mChannel->mTarget << "\"." );
if( e.mTimeAccessor->mCount > 0 )
{
startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0));
endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0));
}
}
std::vector<aiMatrix4x4> resultTrafos;
if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 )
{
std::vector<Collada::Transform> transforms = srcNode->mTransforms;
ai_real time = startTime;
while( 1)
{
for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
{
Collada::ChannelEntry& e = *it;
size_t pos = 0;
ai_real postTime = 0.0;
while( 1)
{
if( pos >= e.mTimeAccessor->mCount)
break;
postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
if( postTime >= time)
break;
++pos;
}
pos = std::min( pos, e.mTimeAccessor->mCount-1);
ai_real temp[16];
for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c);
if( postTime > time && pos > 0)
{
ai_real preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0);
ai_real factor = (time - postTime) / (preTime - postTime);
for( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
{
ai_real v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c);
temp[c] += (v - temp[c]) * factor;
}
}
std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement);
}
aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms);
mat.d4 = time;
resultTrafos.push_back( mat);
ai_real nextTime = ai_real( 1e20 );
for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
{
Collada::ChannelEntry& channelElement = *it;
size_t pos = 0;
while( pos < channelElement.mTimeAccessor->mCount)
{
const ai_real t = ReadFloat( *channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0);
if( t > time)
{
nextTime = std::min( nextTime, t);
break;
}
++pos;
}
if (transforms[channelElement.mTransformIndex].mType == Collada::TF_ROTATE && channelElement.mSubElement == 3 && pos > 0 && pos < channelElement.mTimeAccessor->mCount) {
const ai_real cur_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos, 0);
const ai_real last_key_angle = ReadFloat(*channelElement.mValueAccessor, *channelElement.mValueData, pos - 1, 0);
const ai_real cur_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos, 0);
const ai_real last_key_time = ReadFloat(*channelElement.mTimeAccessor, *channelElement.mTimeData, pos - 1, 0);
const ai_real last_eval_angle = last_key_angle + (cur_key_angle - last_key_angle) * (time - last_key_time) / (cur_key_time - last_key_time);
const ai_real delta = std::abs(cur_key_angle - last_eval_angle);
if (delta >= 180.0) {
const int subSampleCount = static_cast<int>(std::floor(delta / 90.0));
if (cur_key_time != time) {
const ai_real nextSampleTime = time + (cur_key_time - time) / subSampleCount;
nextTime = std::min(nextTime, nextSampleTime);
}
}
}
}
if( nextTime > 1e19)
break;
time = nextTime;
}
}
if( !resultTrafos.empty() )
{
aiNodeAnim* dstAnim = new aiNodeAnim;
dstAnim->mNodeName = nodeName;
dstAnim->mNumPositionKeys = resultTrafos.size();
dstAnim->mNumRotationKeys= resultTrafos.size();
dstAnim->mNumScalingKeys = resultTrafos.size();
dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
for( size_t a = 0; a < resultTrafos.size(); ++a)
{
aiMatrix4x4 mat = resultTrafos[a];
double time = double( mat.d4); mat.d4 = 1.0f;
dstAnim->mPositionKeys[a].mTime = time;
dstAnim->mRotationKeys[a].mTime = time;
dstAnim->mScalingKeys[a].mTime = time;
mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
}
anims.push_back( dstAnim);
} else
{
DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter.");
}
if( !entries.empty() && entries.front().mTimeAccessor->mCount > 0 )
{
std::vector<Collada::ChannelEntry> morphChannels;
for( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
{
Collada::ChannelEntry& e = *it;
if (e.mTargetId.empty())
continue;
if (e.mTargetId.find("morph-weights") != std::string::npos)
morphChannels.push_back(e);
}
if (morphChannels.size() > 0)
{
aiMeshMorphAnim *morphAnim = new aiMeshMorphAnim;
morphAnim->mName.Set(nodeName);
std::vector<MorphTimeValues> morphTimeValues;
int morphAnimChannelIndex = 0;
for( std::vector<Collada::ChannelEntry>::iterator it = morphChannels.begin(); it != morphChannels.end(); ++it)
{
Collada::ChannelEntry& e = *it;
std::string::size_type apos = e.mTargetId.find('(');
std::string::size_type bpos = e.mTargetId.find(')');
if (apos == std::string::npos || bpos == std::string::npos)
continue;
for (unsigned int i = 0; i < e.mTimeData->mValues.size(); i++)
insertMorphTimeValue(morphTimeValues, e.mTimeData->mValues.at(i), e.mValueData->mValues.at(i), morphAnimChannelIndex);
++morphAnimChannelIndex;
}
morphAnim->mNumKeys = morphTimeValues.size();
morphAnim->mKeys = new aiMeshMorphKey[morphAnim->mNumKeys];
for (unsigned int key = 0; key < morphAnim->mNumKeys; key++)
{
morphAnim->mKeys[key].mNumValuesAndWeights = morphChannels.size();
morphAnim->mKeys[key].mValues = new unsigned int [morphChannels.size()];
morphAnim->mKeys[key].mWeights = new double [morphChannels.size()];
morphAnim->mKeys[key].mTime = morphTimeValues[key].mTime;
for (unsigned int valueIndex = 0; valueIndex < morphChannels.size(); valueIndex++)
{
morphAnim->mKeys[key].mValues[valueIndex] = valueIndex;
morphAnim->mKeys[key].mWeights[valueIndex] = getWeightAtKey(morphTimeValues, key, valueIndex);
}
}
morphAnims.push_back(morphAnim);
}
}
}
if( !anims.empty() || !morphAnims.empty())
{
aiAnimation* anim = new aiAnimation;
anim->mName.Set( pName);
anim->mNumChannels = anims.size();
if (anim->mNumChannels > 0)
{
anim->mChannels = new aiNodeAnim*[anims.size()];
std::copy( anims.begin(), anims.end(), anim->mChannels);
}
anim->mNumMorphMeshChannels = morphAnims.size();
if (anim->mNumMorphMeshChannels > 0)
{
anim->mMorphMeshChannels = new aiMeshMorphAnim*[anim->mNumMorphMeshChannels];
std::copy( morphAnims.begin(), morphAnims.end(), anim->mMorphMeshChannels);
}
anim->mDuration = 0.0f;
for( size_t a = 0; a < anims.size(); ++a)
{
anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime);
anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime);
}
for (size_t a = 0; a < morphAnims.size(); ++a)
{
anim->mDuration = std::max(anim->mDuration, morphAnims[a]->mKeys[morphAnims[a]->mNumKeys-1].mTime);
}
anim->mTicksPerSecond = 1;
mAnims.push_back( anim);
}
}
void ColladaLoader::AddTexture ( aiMaterial& mat, const ColladaParser& pParser,
const Collada::Effect& effect,
const Collada::Sampler& sampler,
aiTextureType type, unsigned int idx)
{
const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName );
mat.AddProperty( &name, _AI_MATKEY_TEXTURE_BASE, type, idx );
int map = aiTextureMapMode_Clamp;
if (sampler.mWrapU)
map = aiTextureMapMode_Wrap;
if (sampler.mWrapU && sampler.mMirrorU)
map = aiTextureMapMode_Mirror;
mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx);
map = aiTextureMapMode_Clamp;
if (sampler.mWrapV)
map = aiTextureMapMode_Wrap;
if (sampler.mWrapV && sampler.mMirrorV)
map = aiTextureMapMode_Mirror;
mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx);
mat.AddProperty(&sampler.mTransform, 1,
_AI_MATKEY_UVTRANSFORM_BASE, type, idx);
mat.AddProperty((int*)&sampler.mOp , 1,
_AI_MATKEY_TEXBLEND_BASE, type, idx);
mat.AddProperty((ai_real*)&sampler.mWeighting , 1,
_AI_MATKEY_TEXBLEND_BASE, type, idx);
if (sampler.mUVId != UINT_MAX)
map = sampler.mUVId;
else {
map = -1;
for (std::string::const_iterator it = sampler.mUVChannel.begin();it != sampler.mUVChannel.end(); ++it){
if (IsNumeric(*it)) {
map = strtoul10(&(*it));
break;
}
}
if (-1 == map) {
DefaultLogger::get()->warn("Collada: unable to determine UV channel for texture");
map = 0;
}
}
mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx);
}
void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* )
{
for (auto &elem : newMats)
{
aiMaterial& mat = (aiMaterial&)*elem.second;
Collada::Effect& effect = *elem.first;
int shadeMode;
if (effect.mFaceted)
shadeMode = aiShadingMode_Flat;
else {
switch( effect.mShadeType)
{
case Collada::Shade_Constant:
shadeMode = aiShadingMode_NoShading;
break;
case Collada::Shade_Lambert:
shadeMode = aiShadingMode_Gouraud;
break;
case Collada::Shade_Blinn:
shadeMode = aiShadingMode_Blinn;
break;
case Collada::Shade_Phong:
shadeMode = aiShadingMode_Phong;
break;
default:
DefaultLogger::get()->warn("Collada: Unrecognized shading mode, using gouraud shading");
shadeMode = aiShadingMode_Gouraud;
break;
}
}
mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
shadeMode = effect.mDoubleSided;
mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_TWOSIDED);
shadeMode = effect.mWireframe;
mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME);
mat.AddProperty( &effect.mAmbient, 1,AI_MATKEY_COLOR_AMBIENT);
mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
mat.AddProperty( &effect.mSpecular, 1,AI_MATKEY_COLOR_SPECULAR);
mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
mat.AddProperty( &effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE);
mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS);
mat.AddProperty( &effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY);
mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI);
if(effect.mTransparency >= 0.f && effect.mTransparency <= 1.f) {
if(effect.mRGBTransparency) {
effect.mTransparency *= (
0.212671f * effect.mTransparent.r +
0.715160f * effect.mTransparent.g +
0.072169f * effect.mTransparent.b
);
effect.mTransparent.a = 1.f;
mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT );
} else {
effect.mTransparency *= effect.mTransparent.a;
}
if(effect.mInvertTransparency) {
effect.mTransparency = 1.f - effect.mTransparency;
}
if (effect.mHasTransparency || effect.mTransparency < 1.f) {
mat.AddProperty( &effect.mTransparency, 1, AI_MATKEY_OPACITY );
}
}
if( !effect.mTexAmbient.mName.empty())
AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP);
if( !effect.mTexEmissive.mName.empty())
AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE);
if( !effect.mTexSpecular.mName.empty())
AddTexture( mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR);
if( !effect.mTexDiffuse.mName.empty())
AddTexture( mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE);
if( !effect.mTexBump.mName.empty())
AddTexture( mat, pParser, effect, effect.mTexBump, aiTextureType_NORMALS);
if( !effect.mTexTransparent.mName.empty())
AddTexture( mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY);
if( !effect.mTexReflective.mName.empty())
AddTexture( mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION);
}
}
void ColladaLoader::BuildMaterials( ColladaParser& pParser, aiScene* )
{
newMats.reserve(pParser.mMaterialLibrary.size());
for( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt)
{
const Collada::Material& material = matIt->second;
ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find( material.mEffect);
if( effIt == pParser.mEffectLibrary.end())
continue;
Collada::Effect& effect = effIt->second;
aiMaterial* mat = new aiMaterial;
aiString name( material.mName.empty() ? matIt->first : material.mName );
mat->AddProperty(&name,AI_MATKEY_NAME);
mMaterialIndexByName[matIt->first] = newMats.size();
newMats.push_back( std::pair<Collada::Effect*, aiMaterial*>( &effect,mat) );
}
#if 0#endif
}
aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser,
const Collada::Effect& pEffect, const std::string& pName)
{
aiString result;
std::string name = pName;
while( 1)
{
Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find( name);
if( it == pEffect.mParams.end())
break;
name = it->second.mReference;
}
ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name);
if( imIt == pParser.mImageLibrary.end())
{
DefaultLogger::get()->warn("Collada: Unable to resolve effect texture entry \"" + pName + "\", ended up at ID \"" + name + "\".");
result.Set(name + ".jpg");
ConvertPath(result);
return result;
}
if (imIt->second.mFileName.empty())
{
if (imIt->second.mImageData.empty()) {
throw DeadlyImportError("Collada: Invalid texture, no data or file reference given");
}
aiTexture* tex = new aiTexture();
if (imIt->second.mEmbeddedFormat.length() > 3) {
DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters");
}
strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3);
tex->mHeight = 0;
tex->mWidth = static_cast<unsigned int>(imIt->second.mImageData.size());
tex->pcData = (aiTexel*)new char[tex->mWidth];
memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
result.data[0] = '*';
result.length = 1 + ASSIMP_itoa10(result.data+1,static_cast<unsigned int>(MAXLEN-1),static_cast<int32_t>(mTextures.size()));
mTextures.push_back(tex);
}
else
{
result.Set( imIt->second.mFileName );
ConvertPath(result);
}
return result;
}
void ColladaLoader::ConvertPath (aiString& ss)
{
if (0 == strncmp(ss.data,"file://",7))
{
ss.length -= 7;
memmove(ss.data,ss.data+7,ss.length);
ss.data[ss.length] = '\0';
}
if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' )
{
ss.length--;
memmove( ss.data, ss.data+1, ss.length);
ss.data[ss.length] = 0;
}
char* out = ss.data;
for( const char* it = ss.data; it != ss.data + ss.length; )
{
if( *it == '%' && (it + 3) < ss.data + ss.length )
{
char mychar[3] = { it[1], it[2], 0 };
size_t nbr = strtoul16( mychar);
it += 3;
*out++ = (char)(nbr & 0xFF);
} else
{
*out++ = *it++;
}
}
*out = 0;
ss.length = (ptrdiff_t) (out - ss.data);
}
ai_real ColladaLoader::ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const
{
size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
ai_assert( pos < pData.mValues.size());
return pData.mValues[pos];
}
const std::string& ColladaLoader::ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const
{
size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset;
ai_assert( pos < pData.mStrings.size());
return pData.mStrings[pos];
}
void ColladaLoader::CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const
{
poNodes.push_back( pNode);
for( size_t a = 0; a < pNode->mNumChildren; ++a)
CollectNodes( pNode->mChildren[a], poNodes);
}
const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const std::string& pName) const
{
if( pNode->mName == pName || pNode->mID == pName)
return pNode;
for( size_t a = 0; a < pNode->mChildren.size(); ++a)
{
const Collada::Node* node = FindNode( pNode->mChildren[a], pName);
if( node)
return node;
}
return NULL;
}
const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const
{
if( pNode->mSID == pSID)
return pNode;
for( size_t a = 0; a < pNode->mChildren.size(); ++a)
{
const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID);
if( node)
return node;
}
return NULL;
}
std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode)
{
if (!pNode->mID.empty())
return pNode->mID;
else if (!pNode->mSID.empty())
return pNode->mSID;
else
{
return format() << "$ColladaAutoName$_" << mNodeNameCounter++;
}
}
#endif