Further work on the IRR, AC, LWS loaders. Further work on the - still unfinished - OptimizeGraph step. SceneCombiner works now properly in all cases I tested yet.

Added missing 'typename' in Colladaparser.h
First implementation of spherical and cylindrical mapping, already in use for IRR and LWO models. For the latter the coordinate system is not yet correct.
Moved vec2d to a separate header and added operators similar to vec3.
Added plane and ray helper classes. Just the data is wrapped, no operators required for the moment.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@249 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2008-11-26 13:17:39 +00:00
parent 9ca66fb999
commit fd9e6edc19
45 changed files with 2657 additions and 869 deletions

View File

@ -50,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "ACLoader.h"
#include "ParsingUtils.h"
#include "fast_atof.h"
//#include "Subdivision.h"
using namespace Assimp;
@ -159,7 +159,7 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
Object& obj = objects.back();
aiLight* light = NULL;
if (!ASSIMP_stricmp(buffer,"light"))
if (!ASSIMP_strincmp(buffer,"light",5))
{
// This is a light source. Add it to the list
mLights->push_back(light = new aiLight());
@ -173,7 +173,22 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
light->mName.length = ::sprintf(light->mName.data,"ACLight_%i",mLights->size()-1);
obj.name = std::string( light->mName.data );
DefaultLogger::get()->debug("AC3D: Light source encountered");
obj.type = Object::Light;
}
else if (!ASSIMP_strincmp(buffer,"group",5))
{
obj.type = Object::Group;
}
else if (!ASSIMP_strincmp(buffer,"poly",4))
{
obj.type = Object::Poly;
}
else if (!ASSIMP_strincmp(buffer,"world",5))
{
obj.type = Object::World;
}
while (GetNextLine())
{
@ -213,6 +228,11 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
SkipSpaces(&buffer);
AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat);
}
else if (TokenMatch(buffer,"texoff",6))
{
SkipSpaces(&buffer);
AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset);
}
else if (TokenMatch(buffer,"rot",3))
{
SkipSpaces(&buffer);
@ -223,6 +243,11 @@ void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
SkipSpaces(&buffer);
AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation);
}
else if (TokenMatch(buffer,"subdiv",6))
{
SkipSpaces(&buffer);
obj.subDiv = strtol10(buffer,&buffer);
}
else if (TokenMatch(buffer,"numvert",7))
{
SkipSpaces(&buffer);
@ -352,6 +377,17 @@ void AC3DImporter::ConvertMaterial(const Object& object,
{
s.Set(object.texture);
matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
// UV transformation
if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y ||
object.texOffset.x || object.texOffset.y)
{
aiUVTransform transform;
transform.mScaling = object.texRepeat;
transform.mTranslation = object.texOffset;
matDest.AddProperty<float>((float*)&transform,sizeof(aiUVTransform),
AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
}
}
matDest.AddProperty<aiColor3D>(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE);
@ -377,9 +413,11 @@ void AC3DImporter::ConvertMaterial(const Object& object,
aiNode* AC3DImporter::ConvertObjectSection(Object& object,
std::vector<aiMesh*>& meshes,
std::vector<MaterialHelper*>& outMaterials,
const std::vector<Material>& materials)
const std::vector<Material>& materials,
aiNode* parent)
{
aiNode* node = new aiNode();
node->mParent = parent;
if (object.vertices.size())
{
if (!object.surfaces.size() || !object.numRefs)
@ -434,7 +472,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
register unsigned int idx = (*it).mat;
if (idx >= needMat.size())
{
DefaultLogger::get()->error("AC3D: material index os out of range");
DefaultLogger::get()->error("AC3D: material index is out of range");
idx = 0;
}
if ((*it).entries.empty())
@ -539,13 +577,14 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
face.mIndices[i] = cur++;
// copy vertex positions
*vertices = object.vertices[entry.first];
*vertices = object.vertices[entry.first] + object.translation;
// copy texture coordinates (apply the UV offset)
// copy texture coordinates
if (uv)
{
uv->x = entry.second.x * object.texRepeat.x;
uv->y = entry.second.y * object.texRepeat.y;
uv->x = entry.second.x;
uv->y = entry.second.y;
++uv;
}
}
@ -571,11 +610,11 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
// copy vertex positions
*vertices++ = object.vertices[(*it2).first];
// copy texture coordinates (apply the UV offset)
// copy texture coordinates
if (uv)
{
uv->x = (*it2).second.x * object.texRepeat.x;
uv->y = (*it2).second.y * object.texRepeat.y;
uv->x = (*it2).second.x;
uv->y = (*it2).second.y;
++uv;
}
@ -591,18 +630,65 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
if (uv)
{
uv->x = (*it2).second.x * object.texRepeat.x;
uv->y = (*it2).second.y * object.texRepeat.y;
uv->x = (*it2).second.x;
uv->y = (*it2).second.y;
++uv;
}
}
}
}
}
#if 0
// Now apply catmull clark subdivision if necessary. However, this is
// not *absolutely* correct: it might be we split a mesh up into
// multiple sub meshes, one for each material. AC3D doesn't do that
// in its subdivision implementation, so our output *could* look
// different in some cases.
if (object.subDiv)
{
Subdivider* div = Subdivider::Create(Subdivider::CATMULL_CLARKE);
div->Subdivide(mesh,object.subDiv);
delete div;
}
#endif
}
}
}
if (object.name.length())
node->mName.Set(object.name);
else
{
// generate a name depending on the type of the node
switch (object.type)
{
case Object::Group:
node->mName.length = ::sprintf(node->mName.data,"ACGroup_%i",groups++);
break;
case Object::Poly:
node->mName.length = ::sprintf(node->mName.data,"ACPoly_%i",polys++);
break;
case Object::Light:
node->mName.length = ::sprintf(node->mName.data,"ACLight_%i",lights++);
break;
// there shouldn't be more than one world, but we don't care
case Object::World:
node->mName.length = ::sprintf(node->mName.data,"ACWorld_%i",worlds++);
break;
}
}
// setup the local transformation matrix of the object
// compute the transformation offset to the parent node
node->mTransformation = aiMatrix4x4 ( object.rotation );
node->mTransformation.a4 = object.translation.x;
node->mTransformation.b4 = object.translation.y;
node->mTransformation.c4 = object.translation.z;
// add children to the object
if (object.children.size())
{
@ -610,20 +696,10 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object,
node->mChildren = new aiNode*[node->mNumChildren];
for (unsigned int i = 0; i < node->mNumChildren;++i)
{
node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials);
node->mChildren[i]->mParent = node;
node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node);
}
}
node->mName.Set(object.name);
// setup the local transformation matrix of the object
node->mTransformation = aiMatrix4x4 ( object.rotation );
node->mTransformation.a4 = object.translation.x;
node->mTransformation.b4 = -object.translation.y;
node->mTransformation.c4 = object.translation.z;
return node;
}
@ -653,6 +729,8 @@ void AC3DImporter::InternReadFile( const std::string& pFile,
buffer = &mBuffer2[0];
mNumMeshes = 0;
lights = polys = worlds = groups = 0;
if (::strncmp(buffer,"AC3D",4))
throw new ImportErrorException("AC3D: No valid AC3D file, magic sequence not found");
@ -731,7 +809,7 @@ void AC3DImporter::InternReadFile( const std::string& pFile,
if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4))
pScene->mRootNode->mName.Set("<AC3DWorld>");
// build output arrays
// copy meshes
if (meshes.empty())
{
throw new ImportErrorException("An unknown error occured during converting");
@ -740,11 +818,12 @@ void AC3DImporter::InternReadFile( const std::string& pFile,
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*));
// copy materials
pScene->mNumMaterials = (unsigned int)omaterials.size();
pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*));
// copy lights
pScene->mNumLights = (unsigned int)lights.size();
if (lights.size())
{

View File

@ -113,9 +113,20 @@ protected:
// Represents an AC3D object
struct Object
{
enum Type
{
World = 0x0,
Poly = 0x1,
Group = 0x2,
Light = 0x4
} type;
Object()
: texRepeat(1.f,1.f)
, numRefs (0)
, subDiv (0)
, type (World)
{}
// name of the object
@ -128,7 +139,7 @@ protected:
std::string texture;
// texture repat factors (scaling for all coordinates)
aiVector2D texRepeat;
aiVector2D texRepeat, texOffset;
// rotation matrix
aiMatrix3x3 rotation;
@ -144,6 +155,10 @@ protected:
// number of indices (= num verts in verbose format)
unsigned int numRefs;
// number of subdivisions to be performed on the
// imported data
unsigned int subDiv;
};
@ -208,7 +223,8 @@ private:
aiNode* ConvertObjectSection(Object& object,
std::vector<aiMesh*>& meshes,
std::vector<MaterialHelper*>& outMaterials,
const std::vector<Material>& materials);
const std::vector<Material>& materials,
aiNode* parent = NULL);
// -------------------------------------------------------------------
@ -239,6 +255,9 @@ private:
// current list of light sources
std::vector<aiLight*>* mLights;
// name counters
unsigned int lights, groups, polys, worlds;
};
} // end of namespace Assimp

View File

@ -109,14 +109,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# include "../include/BoostWorkaround/boost/scoped_ptr.hpp"
# include "../include/BoostWorkaround/boost/scoped_array.hpp"
# include "../include/BoostWorkaround/boost/format.hpp"
# include "../include/BoostWorkaround/boost/common_factor_rt.hpp"
# include "../include/BoostWorkaround/boost/foreach.hpp"
#else
# include <boost/scoped_ptr.hpp>
# include <boost/scoped_array.hpp>
# include <boost/format.hpp>
# include <boost/math/common_factor_rt.hpp>
# include <boost/foreach.hpp>
#endif

View File

@ -175,6 +175,9 @@ struct BatchData
// List of all imports
std::list<LoadRequest> requests;
// Base path
std::string pathBase;
};
// ------------------------------------------------------------------------------------------------
@ -202,21 +205,64 @@ BatchLoader::~BatchLoader()
delete data;
}
// ------------------------------------------------------------------------------------------------
void BatchLoader::SetBasePath (const std::string& pBase)
{
BatchData* data = ( BatchData* )pimpl;
data->pathBase = pBase;
// file name? we just need the directory
std::string::size_type ss,ss2;
if (std::string::npos != (ss = data->pathBase.find_first_of('.')))
{
if (std::string::npos != (ss2 = data->pathBase.find_last_of('\\')) ||
std::string::npos != (ss2 = data->pathBase.find_last_of('/')))
{
if (ss > ss2)
data->pathBase.erase(ss2,data->pathBase.length()-ss2);
}
else return;
}
// make sure the directory is terminated properly
char s;
if ((s = *(data->pathBase.end()-1)) != '\\' && s != '/')
data->pathBase.append("\\");
}
// ------------------------------------------------------------------------------------------------
void BatchLoader::AddLoadRequest (const std::string& file,
unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/)
{
ai_assert(!file.empty());
// no threaded implementation for the moment
BatchData* data = ( BatchData* )pimpl;
std::list<LoadRequest>::iterator it = std::find(data->requests.begin(),
data->requests.end(), file);
if (it != data->requests.end())
std::string real;
// build a full path if this is a relative path and
// we have a new base directory given
if (file.length() > 2 && file[1] != ':' && data->pathBase.length())
{
real = data->pathBase + file;
}
else real = file;
// check whether we have this loading request already
std::list<LoadRequest>::iterator it;
for (it = data->requests.begin();it != data->requests.end(); ++it)
{
// Call IOSystem's path comparison function here
if (data->pIOSystem->ComparePaths((*it).file,real))
{
(*it).refCnt++;
return;
}
data->requests.push_back(LoadRequest(file,steps,map));
}
// no, we don't have it. So add it to the queue ...
data->requests.push_back(LoadRequest(real,steps,map));
}
// ------------------------------------------------------------------------------------------------
@ -224,9 +270,19 @@ aiScene* BatchLoader::GetImport (const std::string& file)
{
// no threaded implementation for the moment
BatchData* data = ( BatchData* )pimpl;
std::list<LoadRequest>::iterator it = std::find(data->requests.begin(),
data->requests.end(), file);
if (it != data->requests.end() && (*it).loaded)
std::string real;
// build a full path if this is a relative path and
// we have a new base directory given
if (file.length() > 2 && file[1] != ':' && data->pathBase.length())
{
real = data->pathBase + file;
}
else real = file;
for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it)
{
// Call IOSystem's path comparison function here
if (data->pIOSystem->ComparePaths((*it).file,real) && (*it).loaded)
{
aiScene* sc = (*it).scene;
if (!(--(*it).refCnt))
@ -235,6 +291,7 @@ aiScene* BatchLoader::GetImport (const std::string& file)
}
return sc;
}
}
return NULL;
}
@ -257,9 +314,16 @@ void BatchLoader::LoadAll()
data->pImporter->mIntProperties = (*it).map.ints;
data->pImporter->mStringProperties = (*it).map.strings;
if (!DefaultLogger::isNullLogger())
{
DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%");
DefaultLogger::get()->info("File: " + (*it).file);
}
data->pImporter->ReadFile((*it).file,pp);
(*it).scene = const_cast<aiScene*>(data->pImporter->GetOrphanedScene());
(*it).loaded = true;
DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%");
}
}

View File

@ -275,6 +275,17 @@ public:
~BatchLoader();
/** Sets the base path to be used for all subsequent load
* calls. This is the working directory of Assimp.
*
* Every (inplicit) occurence of '.\' will be replaced with it.
*
* @param pBase Base path. This *may* also be the path to
* a file (the directory of the file is taken then, of course)
*/
void SetBasePath (const std::string& pBase);
/** Add a new file to the list of files to be loaded.
*
* @param file File to be loaded

View File

@ -293,7 +293,7 @@ protected:
template <typename Type>
const Type& ColladaParser::ResolveLibraryReference( const std::map<std::string, Type>& pLibrary, const std::string& pURL) const
{
std::map<std::string, Type>::const_iterator it = pLibrary.find( pURL);
typename std::map<std::string, Type>::const_iterator it = pLibrary.find( pURL);
if( it == pLibrary.end())
ThrowException( boost::str( boost::format( "Unable to resolve library reference \"%s\".") % pURL));
return it->second;

View File

@ -70,31 +70,305 @@ bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const
}
// ------------------------------------------------------------------------------------------------
unsigned int ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,aiAxis axis)
// Compute the AABB of a mesh
inline void FindAABB (aiMesh* mesh, aiVector3D& min, aiVector3D& max)
{
DefaultLogger::get()->error("Mapping type currently not implemented");
return 0;
min = aiVector3D (10e10f, 10e10f, 10e10f);
max = aiVector3D (-10e10f,-10e10f,-10e10f);
for (unsigned int i = 0;i < mesh->mNumVertices;++i)
{
const aiVector3D& v = mesh->mVertices[i];
min.x = ::std::min(v.x,min.x);
min.y = ::std::min(v.y,min.y);
min.z = ::std::min(v.z,min.z);
max.x = ::std::max(v.x,max.x);
max.y = ::std::max(v.y,max.y);
max.z = ::std::max(v.z,max.z);
}
}
// ------------------------------------------------------------------------------------------------
unsigned int ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,aiAxis axis)
// Helper function to determine the 'real' center of a mesh
inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
{
DefaultLogger::get()->error("Mapping type currently not implemented");
return 0;
FindAABB(mesh,min,max);
out = min + (max-min)*0.5f;
}
// ------------------------------------------------------------------------------------------------
unsigned int ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,aiAxis axis)
// Helper function to determine the 'real' center of a mesh
inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
{
DefaultLogger::get()->error("Mapping type currently not implemented");
return 0;
aiVector3D min,max;
FindMeshCenter(mesh,out,min,max);
}
// ------------------------------------------------------------------------------------------------
unsigned int ComputeUVMappingProcess::ComputeBoxMapping(aiMesh* mesh)
// Check whether a ray intersects a plane and find the intersection point
inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos,
const aiVector3D& planeNormal, aiVector3D& pos)
{
const float b = planeNormal * (planePos - ray.pos);
float h = ray.dir * planeNormal;
if (h < 10e-5f && h > -10e-5f || (h = b/h) < 0)
return false;
pos = ray.pos + (ray.dir * h);
return true;
}
// ------------------------------------------------------------------------------------------------
// Find the first empty UV channel in a mesh
inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
{
for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m)
if (!mesh->mTextureCoords[m])return m;
DefaultLogger::get()->error("Unable to compute UV coordinates, no free UV slot found");
return 0xffffffff;
}
// ------------------------------------------------------------------------------------------------
// Try to remove UV seams
void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
{
// TODO: just a very rough algorithm. I think it could be done
// much easier, but I don't know how and am currently too tired to
// to think about a better solution.
const static float LOWER_LIMIT = 0.1f;
const static float UPPER_LIMIT = 0.9f;
const static float LOWER_EPSILON = 1e-3f;
const static float UPPER_EPSILON = 1.f-1e-3f;
for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx)
{
const aiFace& face = mesh->mFaces[fidx];
if (face.mNumIndices < 3) continue; // triangles and polygons only, please
unsigned int small = face.mNumIndices, large = small;
bool zero = false, one = false, round_to_zero = false;
// Check whether this face lies on a UV seam. We can just guess,
// but the assumption that a face with at least one very small
// on the one side and one very large U coord on the other side
// lies on a UV seam should work for most cases.
for (unsigned int n = 0; n < face.mNumIndices;++n)
{
if (out[face.mIndices[n]].x < LOWER_LIMIT)
{
small = n;
// If we have a U value very close to 0 we can't
// round the others to 0, too.
if (out[face.mIndices[n]].x <= LOWER_EPSILON)
zero = true;
else round_to_zero = true;
}
if (out[face.mIndices[n]].x > UPPER_LIMIT)
{
large = n;
// If we have a U value very close to 1 we can't
// round the others to 1, too.
if (out[face.mIndices[n]].x >= UPPER_EPSILON)
one = true;
}
}
if (small != face.mNumIndices && large != face.mNumIndices)
{
for (unsigned int n = 0; n < face.mNumIndices;++n)
{
// If the u value is over the upper limit and no other u
// value of that face is 0, round it to 0
if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero)
out[face.mIndices[n]].x = 0.f;
// If the u value is below the lower limit and no other u
// value of that face is 1, round it to 1
else if (out[face.mIndices[n]].x < LOWER_LIMIT && !one)
out[face.mIndices[n]].x = 1.f;
// The face contains both 0 and 1 as UV coords. This can occur
// for faces which have an edge that lies directly on the seam.
// Due to numerical inaccuracies one U coord becomes 0, the
// other 1. But we do still have a third UV coord to determine
// to which side we must round to.
else if (one && zero)
{
if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON)
out[face.mIndices[n]].x = 0.f;
else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON)
out[face.mIndices[n]].x = 1.f;
}
}
}
}
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,aiAxis axis, aiVector3D* out)
{
aiVector3D center;
FindMeshCenter (mesh, center);
// For each point get a normalized projection vector in the sphere,
// get its longitude and latitude and map them to their respective
// UV axes. Problems occur around the poles ... unsolvable.
//
// The spherical coordinate system looks like this:
// x = cos(lon)*cos(lat)
// y = sin(lon)*cos(lat)
// z = sin(lat)
//
// Thus we can derive:
// lat = arcsin (z)
// lon = arctan (y/x)
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)
{
const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
float lat, lon;
switch (axis)
{
case aiAxis_X:
lat = asin (diff.x);
lon = atan2 (diff.z, diff.y);
break;
case aiAxis_Y:
lat = asin (diff.y);
lon = atan2 (diff.x, diff.z);
break;
case aiAxis_Z:
lat = asin (diff.z);
lon = atan2 (diff.y, diff.x);
break;
}
out[pnt] = aiVector3D((lon + (float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI,
(lat + (float)AI_MATH_HALF_PI) / (float)AI_MATH_PI, 0.f);
}
// Now find and remove UV seams. A seam occurs if a face has a tcoord
// close to zero on the one side, and a tcoord close to one on the
// other side.
RemoveUVSeams(mesh,out);
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,aiAxis axis, aiVector3D* out)
{
aiVector3D center, min, max;
FindMeshCenter(mesh, center, min, max);
ai_assert(0 == aiAxis_X);
const float diff = max[axis] - min[axis];
if (!diff)
{
DefaultLogger::get()->error("Can't compute cylindrical mapping, the mesh is "
"flat in the requested axis");
return;
}
// If the main axis is 'z', the z coordinate of a point 'p' is mapped
// directly to the texture V axis. The other axis is derived from
// the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where
// 'c' is the center point of the mesh.
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)
{
const aiVector3D& pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
switch (axis)
{
case aiAxis_X:
uv.y = (pos.x - min.x) / diff;
uv.x = atan2 ( pos.z - center.z, pos.y - center.y);
break;
case aiAxis_Y:
uv.y = (pos.y - min.y) / diff;
uv.x = atan2 ( pos.x - center.x, pos.z - center.z);
break;
case aiAxis_Z:
uv.y = (pos.z - min.z) / diff;
uv.x = atan2 ( pos.y - center.y, pos.x - center.x);
break;
}
uv.x = (uv.x +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
uv.z = 0.f;
}
// Now find and remove UV seams. A seam occurs if a face has a tcoord
// close to zero on the one side, and a tcoord close to one on the
// other side.
RemoveUVSeams(mesh,out);
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,aiAxis axis, aiVector3D* out)
{
aiVector3D center, min, max;
FindMeshCenter(mesh, center, min, max);
float diffu,diffv;
switch (axis)
{
case aiAxis_X:
diffu = max.z - min.z;
diffv = max.y - min.y;
break;
case aiAxis_Y:
diffu = max.x - min.x;
diffv = max.z - min.z;
break;
case aiAxis_Z:
diffu = max.y - min.y;
diffv = max.z - min.z;
break;
}
if (!diffu || !diffv)
{
DefaultLogger::get()->error("Can't compute plane mapping, the mesh is "
"flat in the requested axis");
return;
}
// That's rather simple. We just project the vertices onto a plane
// that lies on the two coordinate aces orthogonal to the main axis
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)
{
const aiVector3D& pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt];
switch (axis)
{
case aiAxis_X:
uv.x = (pos.z - min.z) / diffu;
uv.y = (pos.y - min.y) / diffv;
break;
case aiAxis_Y:
uv.x = (pos.x - min.x) / diffu;
uv.y = (pos.z - min.z) / diffv;
break;
case aiAxis_Z:
uv.x = (pos.y - min.y) / diffu;
uv.y = (pos.x - min.x) / diffv;
break;
}
uv.z = 0.f;
}
}
// ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeBoxMapping(aiMesh* mesh, aiVector3D* out)
{
DefaultLogger::get()->error("Mapping type currently not implemented");
return 0;
}
// ------------------------------------------------------------------------------------------------
@ -103,17 +377,23 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
DefaultLogger::get()->debug("GenUVCoordsProcess begin");
char buffer[1024];
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
throw new ImportErrorException("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
std::list<MappingInfo> mappingStack;
/* Iterate through all materials and search for non-UV mapped textures
*/
for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
{
mappingStack.clear();
aiMaterial* mat = pScene->mMaterials[i];
for (unsigned int a = 0; a < mat->mNumProperties;++a)
{
aiMaterialProperty* prop = mat->mProperties[a];
if (!::strcmp( prop->mKey.data, "$tex.mapping"))
{
aiTextureMapping mapping = *((aiTextureMapping*)prop->mData);
aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData);
if (aiTextureMapping_UV != mapping)
{
if (!DefaultLogger::isNullLogger())
@ -125,7 +405,10 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
DefaultLogger::get()->info(buffer);
}
aiAxis axis;
if (aiTextureMapping_OTHER == mapping)
continue;
MappingInfo info (mapping);
// Get further properties - currently only the major axis
for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2)
@ -136,11 +419,21 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis"))
{
axis = *((aiAxis*)prop2->mData);
info.axis = *((aiAxis*)prop2->mData);
break;
}
}
unsigned int idx;
// Check whether we have this mapping mode already
std::list<MappingInfo>::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info);
if (mappingStack.end() != it)
{
idx = (*it).uv;
}
else
{
/* We have found a non-UV mapped texture. Now
* we need to find all meshes using this material
* that we can compute UV channels for them.
@ -148,28 +441,51 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
for (unsigned int m = 0; m < pScene->mNumMeshes;++m)
{
aiMesh* mesh = pScene->mMeshes[m];
if (mesh->mMaterialIndex != i) continue;
unsigned int outIdx;
if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == 0xffffffff ||
!mesh->mNumVertices)
{
continue;
}
// Allocate output storage
aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
switch (mapping)
{
case aiTextureMapping_SPHERE:
ComputeSphereMapping(mesh,axis);
ComputeSphereMapping(mesh,info.axis,p);
break;
case aiTextureMapping_CYLINDER:
ComputeCylinderMapping(mesh,axis);
ComputeCylinderMapping(mesh,info.axis,p);
break;
case aiTextureMapping_PLANE:
ComputePlaneMapping(mesh,axis);
ComputePlaneMapping(mesh,info.axis,p);
break;
case aiTextureMapping_BOX:
ComputeBoxMapping(mesh);
ComputeBoxMapping(mesh,p);
break;
default:
ai_assert(false);
}
if (m && idx != outIdx)
{
DefaultLogger::get()->warn("UV index mismatch. Not all meshes assigned to "
"this material have equal numbers of UV channels. The UV index stored in "
"the material structure does therefore not apply for all meshes. ");
}
idx = outIdx;
}
}
}
info.uv = idx;
mappingStack.push_back(info);
}
// Update the material property list
mapping = aiTextureMapping_UV;
((MaterialHelper*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex));
}
}
}
}
DefaultLogger::get()->debug("GenUVCoordsProcess finished");
}

View File

@ -89,35 +89,59 @@ protected:
*
* @param mesh Mesh to be processed
* @param axis Main axis
* @return Index of the newly generated UV channel
* @param out Receives output UV coordinates
*/
unsigned int ComputeSphereMapping(aiMesh* mesh,aiAxis axis);
void ComputeSphereMapping(aiMesh* mesh,aiAxis axis,
aiVector3D* out);
// -------------------------------------------------------------------
/** Computes cylindrical UV coordinates for a mesh
*
* @param mesh Mesh to be processed
* @param axis Main axis
* @return Index of the newly generated UV channel
* @param out Receives output UV coordinates
*/
unsigned int ComputeCylinderMapping(aiMesh* mesh,aiAxis axis);
void ComputeCylinderMapping(aiMesh* mesh,aiAxis axis,
aiVector3D* out);
// -------------------------------------------------------------------
/** Computes planar UV coordinates for a mesh
*
* @param mesh Mesh to be processed
* @param axis Main axis
* @return Index of the newly generated UV channel
* @param out Receives output UV coordinates
*/
unsigned int ComputePlaneMapping(aiMesh* mesh,aiAxis axis);
void ComputePlaneMapping(aiMesh* mesh,aiAxis axis,
aiVector3D* out);
// -------------------------------------------------------------------
/** Computes cubic UV coordinates for a mesh
*
* @param mesh Mesh to be processed
* @return Index of the newly generated UV channel
* @param out Receives output UV coordinates
*/
unsigned int ComputeBoxMapping(aiMesh* mesh);
void ComputeBoxMapping(aiMesh* mesh, aiVector3D* out);
private:
// temporary structure to describe a mapping
struct MappingInfo
{
MappingInfo(aiTextureMapping _type)
: type (_type)
, axis (aiAxis_X)
, uv (0u)
{}
aiTextureMapping type;
aiAxis axis;
unsigned int uv;
bool operator== (const MappingInfo& other)
{
return type == other.type && axis == other.axis;
}
};
};
} // end of namespace Assimp

View File

@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor.
DefaultIOSystem::DefaultIOSystem()
@ -103,3 +104,42 @@ std::string DefaultIOSystem::getOsSeparator() const
#endif
return sep;
}
// ------------------------------------------------------------------------------------------------
// IOSystem default implementation (ComparePaths isn't a pure virtual function)
bool IOSystem::ComparePaths (const std::string& one,
const std::string& second)
{
return !ASSIMP_stricmp(one,second);
}
// this should be sufficient for all platforms :D
#define PATHLIMIT 1024
// ------------------------------------------------------------------------------------------------
// Convert a relative path into an absolute path
inline void MakeAbsolutePath (const std::string& in, char* _out)
{
::_fullpath(_out, in.c_str(),PATHLIMIT);
}
// ------------------------------------------------------------------------------------------------
// DefaultIOSystem's more specialized implementation
bool DefaultIOSystem::ComparePaths (const std::string& one,
const std::string& second)
{
// chances are quite good both paths are formatted identically,
// so we can hopefully return here already
if( !ASSIMP_stricmp(one,second) )
return true;
char temp1[PATHLIMIT];
char temp2[PATHLIMIT];
MakeAbsolutePath (one, temp1);
MakeAbsolutePath (second, temp2);
return !ASSIMP_stricmp(temp1,temp2);
}
#undef PATHLIMIT

View File

@ -73,6 +73,10 @@ public:
// -------------------------------------------------------------------
/** Closes the given file and releases all resources associated with it. */
void Close( IOStream* pFile);
// -------------------------------------------------------------------
/** Compare two paths */
bool ComparePaths (const std::string& one, const std::string& second);
};
} //!ns Assimp

View File

@ -76,6 +76,9 @@ void GenFaceNormalsProcess::Execute( aiScene* pScene)
{
DefaultLogger::get()->debug("GenFaceNormalsProcess begin");
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
throw new ImportErrorException("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{

View File

@ -88,6 +88,9 @@ void GenVertexNormalsProcess::Execute( aiScene* pScene)
{
DefaultLogger::get()->debug("GenVertexNormalsProcess begin");
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
throw new ImportErrorException("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{

View File

@ -51,8 +51,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SceneCombiner.h"
#include "StandardShapes.h"
// We need boost::common_factor to compute the lcm/gcd of a number
#ifdef ASSIMP_BUILD_BOOST_WORKAROUND
# include "../include/BoostWorkaround/boost/common_factor_rt.hpp"
#else
# include <boost/math/common_factor_rt.hpp>
#endif
using namespace Assimp;
// Transformation matrix to convert from Assimp to IRR space
static aiMatrix4x4 AI_TO_IRR_MATRIX = aiMatrix4x4 ( 1.0f, 0.0f, 0.0f,
0.f, 0.0f, 0.0f, -1.0f, 0.f, 0.0f, 1.0f, 0.0f, 0.f, 0.f, 0.f, 0.f, 1.f);
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
@ -163,7 +174,7 @@ aiMesh* IRRImporter::BuildSingleQuadMesh(const SkyboxVertex& v1,
*vec = v4.normal;
// copy texture coordinates
out->mTextureCoords[0] = new aiVector3D[4];
vec = out->mTextureCoords[0] = new aiVector3D[4];
*vec++ = v1.uv;
*vec++ = v2.uv;
*vec++ = v3.uv;
@ -240,12 +251,12 @@ void IRRImporter::BuildSkybox(std::vector<aiMesh*>& meshes, std::vector<aiMateri
SkyboxVertex( l,-l, l, 0, 1, 0, 0.f,0.f),
SkyboxVertex( l,-l,-l, 0, 1, 0, 1.f,0.f),
SkyboxVertex(-l,-l,-l, 0, 1, 0, 1.f,1.f),
SkyboxVertex(-l,-l,-l, 0, 1, 0, 0.f,1.f)) );
SkyboxVertex(-l,-l, l, 0, 1, 0, 0.f,1.f)) );
meshes.back()->mMaterialIndex = materials.size()-1u;
}
// ------------------------------------------------------------------------------------------------
void IRRImporter::CopyMaterial(std::vector<aiMaterial*> materials,
void IRRImporter::CopyMaterial(std::vector<aiMaterial*>& materials,
std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
unsigned int& defMatIdx,
aiMesh* mesh)
@ -297,44 +308,71 @@ inline void FindSuitableMultiple(int& angle)
}
// ------------------------------------------------------------------------------------------------
void IRRImporter::ComputeAnimations(Node* root, std::vector<aiNodeAnim*>& anims,
const aiMatrix4x4& transform)
void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNodeAnim*>& anims)
{
ai_assert(NULL != root);
ai_assert(NULL != root && NULL != real);
if (root->animators.empty())return;
const aiMatrix4x4& transform = real->mTransformation;
typedef std::pair< TemporaryAnim, Animator* > AnimPair;
const unsigned int resolution = 1;
std::vector<AnimPair> temp;
temp.reserve(root->animators.size());
unsigned int total = 0;
for (std::list<Animator>::iterator it = root->animators.begin();
it != root->animators.end(); ++it)
{
if ((*it).type == Animator::UNKNOWN ||
(*it).type == Animator::OTHER)
if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER)
{
DefaultLogger::get()->warn("IRR: Skipping unknown or unsupported animator");
continue;
}
temp.push_back(AnimPair(TemporaryAnim(),&(*it)));
++total;
}
if (!total)return;
else if (1 == total)
{
DefaultLogger::get()->warn("IRR: Generating dummy nodes to simulate multiple animators");
}
if (temp.empty())return;
// NOTE: 1 tick == i millisecond
// All animators are applied one after another. We generate a set of
// transformation matrices for each of it. Then we combine all
// transformation matrices, decompose them and build an output animation.
for (std::vector<AnimPair>::iterator it = temp.begin();
it != temp.end(); ++it)
unsigned int cur = 0;
for (std::list<Animator>::iterator it = root->animators.begin();
it != root->animators.end(); ++it)
{
TemporaryAnim& out = (*it).first;
Animator* in = (*it).second;
if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER)continue;
switch (in->type)
Animator& in = *it ;
aiNodeAnim* anim = new aiNodeAnim();
if (cur != total-1)
{
// Build a new name - a prefix instead of a suffix because it is
// easier to check against
anim->mNodeName.length = ::sprintf(anim->mNodeName.data,
"$INST_DUMMY_%i_%s",total-1,
(root->name.length() ? root->name.c_str() : ""));
// we'll also need to insert a dummy in the node hierarchy.
aiNode* dummy = new aiNode();
for (unsigned int i = 0; i < real->mParent->mNumChildren;++i)
if (real->mParent->mChildren[i] == real)
real->mParent->mChildren[i] = dummy;
dummy->mParent = real->mParent;
dummy->mName = anim->mNodeName;
dummy->mNumChildren = 1;
dummy->mChildren = new aiNode*[dummy->mNumChildren];
dummy->mChildren[0] = real;
// the transformation matrix of the dummy node is the identity
real->mParent = dummy;
}
else anim->mNodeName.Set(root->name);
++cur;
switch (in.type)
{
case Animator::ROTATION:
{
@ -347,17 +385,20 @@ void IRRImporter::ComputeAnimations(Node* root, std::vector<aiNodeAnim*>& anims,
// here in order to get good results.
// -----------------------------------------------------
int angles[3];
angles[0] = (int)(in->direction.x*100);
angles[1] = (int)(in->direction.y*100);
angles[2] = (int)(in->direction.z*100);
angles[0] = (int)(in.direction.x*100);
angles[1] = (int)(in.direction.y*100);
angles[2] = (int)(in.direction.z*100);
angles[0] %= 360;
angles[1] %= 360;
angles[2] %= 360;
if ((angles[0]*angles[1]) && (angles[1]*angles[2]))
{
FindSuitableMultiple(angles[0]);
FindSuitableMultiple(angles[1]);
FindSuitableMultiple(angles[2]);
}
int lcm = 360;
@ -390,87 +431,136 @@ void IRRImporter::ComputeAnimations(Node* root, std::vector<aiNodeAnim*>& anims,
max = std::max(max, (float)lcm / angles[2]);
// Allocate transformation matrices
out.SetupMatrices((unsigned int)(max*fps));
anim->mNumRotationKeys = (unsigned int)(max*fps);
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
// begin with a zero angle
aiVector3D angle;
for (unsigned int i = 0; i < out.last;++i)
for (unsigned int i = 0; i < anim->mNumRotationKeys;++i)
{
// build the rotation matrix for the given euler angles
aiMatrix4x4& m = out.matrices[i];
// build the quaternion for the given euler angles
aiQuatKey& q = anim->mRotationKeys[i];
// we start with the node transformation
m = transform;
aiMatrix4x4 m2;
if (angle.x)
m *= aiMatrix4x4::RotationX(angle.x,m2);
if (angle.y)
m *= aiMatrix4x4::RotationX(angle.y,m2);
if (angle.z)
m *= aiMatrix4x4::RotationZ(angle.z,m2);
q.mValue = aiQuaternion(angle.x, angle.y, angle.z);
q.mTime = (double)i;
// increase the angle
angle += in->direction;
angle += in.direction;
}
// This animation is repeated and repeated ...
out.post = aiAnimBehaviour_REPEAT;
anim->mPostState = aiAnimBehaviour_REPEAT;
anim->mPreState = aiAnimBehaviour_CONSTANT;
}
break;
case Animator::FLY_CIRCLE:
{
anim->mPostState = aiAnimBehaviour_REPEAT;
anim->mPreState = aiAnimBehaviour_CONSTANT;
// -----------------------------------------------------
// Find out how much time we'll need to perform a
// full circle.
// -----------------------------------------------------
const double seconds = (1. / in.speed) / 1000.;
const double tdelta = 1000. / fps;
anim->mNumPositionKeys = (unsigned int) (fps * seconds);
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
// from Irrlicht, what else should we do than copying it?
aiVector3D vecU,vecV;
if (in.direction.y)
{
vecV = aiVector3D(50,0,0) ^ in.direction;
}
else vecV = aiVector3D(0,50,00) ^ in.direction;
vecV.Normalize();
vecU = (vecV ^ in.direction).Normalize();
// build the output keys
for (unsigned int i = 0; i < anim->mNumPositionKeys;++i)
{
aiVectorKey& key = anim->mPositionKeys[i];
key.mTime = i * tdelta;
const float t = (float) ( in.speed * key.mTime );
key.mValue = in.circleCenter + in.circleRadius * ((vecU*::cos(t)) + (vecV*::sin(t)));
}
}
break;
case Animator::FLY_STRAIGHT:
{
anim->mPostState = (in.loop ? aiAnimBehaviour_REPEAT : aiAnimBehaviour_CONSTANT);
anim->mPreState = aiAnimBehaviour_CONSTANT;
const double seconds = in.timeForWay / 1000.;
const double tdelta = 1000. / fps;
anim->mNumPositionKeys = (unsigned int) (fps * seconds);
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
aiVector3D diff = in.direction - in.circleCenter;
const float lengthOfWay = diff.Length();
diff.Normalize();
const double timeFactor = lengthOfWay / in.timeForWay;
// build the output keys
for (unsigned int i = 0; i < anim->mNumPositionKeys;++i)
{
aiVectorKey& key = anim->mPositionKeys[i];
key.mTime = i * tdelta;
key.mValue = in.circleCenter + diff * float(timeFactor * key.mTime);
}
}
break;
case Animator::FOLLOW_SPLINE:
{
out.post = aiAnimBehaviour_REPEAT;
const int size = (int)in->splineKeys.size();
anim->mPostState = aiAnimBehaviour_REPEAT;
anim->mPreState = aiAnimBehaviour_CONSTANT;
const int size = (int)in.splineKeys.size();
if (!size)
{
// We have no point in the spline. That's bad. Really bad.
DefaultLogger::get()->warn("IRR: Spline animators with no points defined");
delete anim;anim = NULL;
break;
}
else if (size == 1)
{
// We have just one point in the spline
out.SetupMatrices(1);
out.matrices[0].a4 = in->splineKeys[0].mValue.x;
out.matrices[0].b4 = in->splineKeys[0].mValue.y;
out.matrices[0].c4 = in->splineKeys[0].mValue.z;
// We have just one point in the spline so we don't need the full calculation
anim->mNumPositionKeys = 1;
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
anim->mPositionKeys[0].mValue = in.splineKeys[0].mValue;
anim->mPositionKeys[0].mTime = 0.f;
break;
}
unsigned int ticksPerFull = 15;
out.SetupMatrices(ticksPerFull*fps);
anim->mNumPositionKeys = (unsigned int) ( ticksPerFull * fps );
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
for (unsigned int i = 0; i < out.last;++i)
for (unsigned int i = 0; i < anim->mNumPositionKeys;++i)
{
aiMatrix4x4& m = out.matrices[i];
aiVectorKey& key = anim->mPositionKeys[i];
const float dt = (i * in->speed * 0.001f );
const float dt = (i * in.speed * 0.001f );
const float u = dt - floor(dt);
const int idx = (int)floor(dt) % size;
// get the 4 current points to evaluate the spline
const aiVector3D& p0 = in->splineKeys[ ClampSpline( idx - 1, size ) ].mValue;
const aiVector3D& p1 = in->splineKeys[ ClampSpline( idx + 0, size ) ].mValue;
const aiVector3D& p2 = in->splineKeys[ ClampSpline( idx + 1, size ) ].mValue;
const aiVector3D& p3 = in->splineKeys[ ClampSpline( idx + 2, size ) ].mValue;
const aiVector3D& p0 = in.splineKeys[ ClampSpline( idx - 1, size ) ].mValue;
const aiVector3D& p1 = in.splineKeys[ ClampSpline( idx + 0, size ) ].mValue;
const aiVector3D& p2 = in.splineKeys[ ClampSpline( idx + 1, size ) ].mValue;
const aiVector3D& p3 = in.splineKeys[ ClampSpline( idx + 2, size ) ].mValue;
// compute polynomials
const float u2 = u*u;
@ -482,54 +572,90 @@ void IRRImporter::ComputeAnimations(Node* root, std::vector<aiNodeAnim*>& anims,
const float h4 = u3 - u2;
// compute the spline tangents
const aiVector3D t1 = ( p2 - p0 ) * in->tightness;
aiVector3D t2 = ( p3 - p1 ) * in->tightness;
const aiVector3D t1 = ( p2 - p0 ) * in.tightness;
aiVector3D t2 = ( p3 - p1 ) * in.tightness;
// and use them to get the interpolated point
t2 = (h1 * p1 + p2 * h2 + t1 * h3 + h4 * t2);
// build a simple translation matrix from it
m.a4 = t2.x;
m.b4 = t2.y;
m.c4 = t2.z;
key.mValue = t2.x;
key.mTime = (double) i;
}
}
break;
};
}
aiNodeAnim* out = new aiNodeAnim();
out->mNodeName.Set(root->name);
if (temp.size() == 1)
if (anim)
{
// If there's just one animator to be processed our
// task is quite easy
TemporaryAnim& one = temp[0].first;
anims.push_back(anim);
++total;
}
}
}
out->mPostState = one.post;
out->mNumPositionKeys = one.last;
out->mNumScalingKeys = one.last;
out->mNumRotationKeys = one.last;
// ------------------------------------------------------------------------------------------------
// This function is maybe more generic than we'd need it here
void SetupMapping (MaterialHelper* mat, aiTextureMapping mode, aiAxis axis = aiAxis_Y)
{
// Check whether there are texture properties defined - setup
// the desired texture mapping mode for all of them and ignore
// all UV settings we might encounter. WE HAVE NO UVS!
out->mPositionKeys = new aiVectorKey[one.last];
out->mScalingKeys = new aiVectorKey[one.last];
out->mRotationKeys = new aiQuatKey[one.last];
std::vector<aiMaterialProperty*> p;
p.reserve(mat->mNumProperties+1);
for (unsigned int i = 0; i < one.last;++i)
for (unsigned int i = 0; i < mat->mNumProperties;++i)
{
aiVectorKey& scaling = out->mScalingKeys[i];
aiVectorKey& position = out->mPositionKeys[i];
aiQuatKey& rotation = out->mRotationKeys[i];
aiMaterialProperty* prop = mat->mProperties[i];
if (!::strcmp( prop->mKey.data, "$tex.file"))
{
// Setup the mapping key
aiMaterialProperty* m = new aiMaterialProperty();
m->mKey.Set("$tex.mapping");
m->mIndex = prop->mIndex;
m->mSemantic = prop->mSemantic;
m->mType = aiPTI_Integer;
scaling.mTime = position.mTime = rotation.mTime = (double)i;
one.matrices[i].Decompose(scaling.mValue, rotation.mValue, position.mValue);
m->mDataLength = 4;
m->mData = new char[4];
*((int*)m->mData) = mode;
p.push_back(prop);
p.push_back(m);
// Setup the mapping axis
if (mode == aiTextureMapping_CYLINDER || mode == aiTextureMapping_PLANE ||
mode == aiTextureMapping_SPHERE)
{
m = new aiMaterialProperty();
m->mKey.Set("$tex.mapaxis");
m->mIndex = prop->mIndex;
m->mSemantic = prop->mSemantic;
m->mType = aiPTI_Integer;
m->mDataLength = 4;
m->mData = new char[4];
*((int*)m->mData) = axis;
p.push_back(m);
}
}
else if (! ::strcmp( prop->mKey.data, "$tex.uvwsrc"))
{
delete mat->mProperties[i];
}
else p.push_back(prop);
}
// NOTE: It is possible that some of the tracks we're returning
// are dummy tracks, but the ScenePreprocessor will fix that, hopefully
if (p.empty())return;
// rebuild the output array
if (p.size() > mat->mNumAllocated)
{
delete[] mat->mProperties;
mat->mProperties = new aiMaterialProperty*[p.size()];
}
mat->mNumProperties = (unsigned int)p.size();
::memcpy(mat->mProperties,&p[0],sizeof(void*)*mat->mNumProperties);
}
// ------------------------------------------------------------------------------------------------
@ -538,10 +664,11 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
std::vector<aiMesh*>& meshes,
std::vector<aiNodeAnim*>& anims,
std::vector<AttachmentInfo>& attach,
std::vector<aiMaterial*> materials,
std::vector<aiMaterial*>& materials,
unsigned int& defMatIdx)
{
unsigned int oldMeshSize = (unsigned int)meshes.size();
unsigned int meshTrafoAssign = 0;
// Now determine the type of the node
switch (root->type)
@ -549,7 +676,10 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
case Node::ANIMMESH:
case Node::MESH:
{
// get the loaded mesh from the scene and add it to
if (!root->meshPath.length())
break;
// Get the loaded mesh from the scene and add it to
// the list of all scenes to be attached to the
// graph we're currently building
aiScene* scene = batch.GetImport(root->meshPath);
@ -560,9 +690,45 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
break;
}
attach.push_back(AttachmentInfo(scene,rootOut));
meshTrafoAssign = 1;
// now combine the material we've loaded for this mesh
// with the real meshes we got from the file. As we
// If the root node of the scene is animated - and *this* node
// is animated, too, we need to insert a dummy node into the
// hierarchy in order to avoid interferences with animations
for (unsigned int i = 0; i < scene->mNumAnimations;++i)
{
aiAnimation* anim = scene->mAnimations[i];
for (unsigned int a = 0; a < anim->mNumChannels;++a)
{
if (scene->mRootNode->mName == anim->mChannels[a]->mNodeName)
{
if (root->animators.empty())
{
meshTrafoAssign = 2;
}
else
{
meshTrafoAssign = 3;
aiNode* dummy = new aiNode();
dummy->mName.Set("$CSpaceSeam$");
dummy->mNumChildren = 1;
dummy->mChildren = new aiNode*[1];
dummy->mChildren[0] = scene->mRootNode;
scene->mRootNode->mParent = dummy;
scene->mRootNode = dummy;
scene->mRootNode->mTransformation = AI_TO_IRR_MATRIX;
}
break;
}
}
}
if (1 == meshTrafoAssign)
scene->mRootNode->mTransformation *= AI_TO_IRR_MATRIX;
// Now combine the material we've loaded for this mesh
// with the real materials we got from the file. As we
// don't execute any pp-steps on the file, the numbers
// should be equal. If they are not, we can impossibly
// do this ...
@ -575,7 +741,7 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
}
for (unsigned int i = 0; i < scene->mNumMaterials;++i)
{
// delete the old material
// Delete the old material, we don't need it anymore
delete scene->mMaterials[i];
std::pair<aiMaterial*, unsigned int>& src = root->materials[i];
@ -599,8 +765,7 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
mesh->mMaterialIndex];
MaterialHelper* mat = (MaterialHelper*)src.first;
if (mesh->HasVertexColors(0) &&
src.second & AI_IRRMESH_MAT_trans_vertex_alpha)
if (mesh->HasVertexColors(0) && src.second & AI_IRRMESH_MAT_trans_vertex_alpha)
{
bool bdo = true;
for (unsigned int a = 1; a < mesh->mNumVertices;++a)
@ -624,14 +789,13 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
}
// If we have a second texture coordinate set and a second texture
// (either lightmap, normalmap, 2layered material we need to
// setup the correct UV index for it). The texture can either
// (either lightmap, normalmap, 2layered material) we need to
// setup the correct UV index for it. The texture can either
// be diffuse (lightmap & 2layer) or a normal map (normal & parallax)
if (mesh->HasTextureCoords(1))
{
int idx = 1;
if (src.second & (AI_IRRMESH_MAT_solid_2layer |
AI_IRRMESH_MAT_lightmap))
if (src.second & (AI_IRRMESH_MAT_solid_2layer | AI_IRRMESH_MAT_lightmap))
{
mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
}
@ -653,11 +817,11 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
case Node::SPHERE:
{
// generate the sphere model. Our input parameter to
// Generate the sphere model. Our input parameter to
// the sphere generation algorithm is the number of
// subdivisions of each triangle - but here we have
// the number of poylgons on a specific axis. Just
// use some limits ...
// use some hardcoded limits to approximate this ...
unsigned int mul = root->spherePolyCountX*root->spherePolyCountY;
if (mul < 100)mul = 2;
else if (mul < 300)mul = 3;
@ -667,10 +831,14 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
&StandardShapes::MakeSphere));
// Adjust scaling
root->scaling *= root->sphereRadius;
root->scaling *= root->sphereRadius/2;
// Copy one output material
CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
// Now adjust this output material - if there is a first texture
// set, setup spherical UV mapping around the Y axis.
SetupMapping ( (MaterialHelper*) materials.back(), aiTextureMapping_SPHERE, aiAxis_Y );
}
break;
@ -685,6 +853,10 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
// Copy one output material
CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
// Now adjust this output material - if there is a first texture
// set, setup cubic UV mapping
SetupMapping ( (MaterialHelper*) materials.back(), aiTextureMapping_BOX );
}
break;
@ -745,10 +917,7 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
// Now compute the final local transformation matrix of the
// node from the given translation, rotation and scaling values.
// (the rotation is given in Euler angles, XYZ order)
aiMatrix4x4 m;
rootOut->mTransformation = aiMatrix4x4::RotationX(AI_DEG_TO_RAD(root->rotation.x),m)
* aiMatrix4x4::RotationY(AI_DEG_TO_RAD(root->rotation.y),m)
* aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(root->rotation.z),m);
rootOut->mTransformation.FromEulerAngles(AI_DEG_TO_RAD(root->rotation) );
// apply scaling
aiMatrix4x4& mat = rootOut->mTransformation;
@ -763,12 +932,15 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
mat.c3 *= root->scaling.z;
// apply translation
mat.a4 = root->position.x;
mat.b4 = root->position.y;
mat.c4 = root->position.z;
mat.a4 += root->position.x;
mat.b4 += root->position.y;
mat.c4 += root->position.z;
if (meshTrafoAssign == 2)
mat *= AI_TO_IRR_MATRIX;
// now compute animations for the node
ComputeAnimations(root,anims,mat);
ComputeAnimations(root,rootOut, anims);
// Add all children recursively. First allocate enough storage
// for them, then call us again
@ -804,6 +976,7 @@ void IRRImporter::InternReadFile( const std::string& pFile,
// The root node of the scene
Node* root = new Node(Node::DUMMY);
root->parent = NULL;
root->name = "<IRRSceneRoot>";
// Current node parent
Node* curParent = root;
@ -819,6 +992,7 @@ void IRRImporter::InternReadFile( const std::string& pFile,
// Batch loader used to load external models
BatchLoader batch(pIOHandler);
batch.SetBasePath(pFile);
cameras.reserve(5);
lights.reserve(5);
@ -848,6 +1022,8 @@ void IRRImporter::InternReadFile( const std::string& pFile,
* and join its animation channels with ours.
* "empty" - A dummy node
* "camera" - A camera
* "terrain" - a terrain node (data comes from a heightmap)
* "billboard", ""
*
* Each of these nodes can be animated and all can have multiple
* materials assigned (except lights, cameras and dummies, of course).
@ -855,8 +1031,9 @@ void IRRImporter::InternReadFile( const std::string& pFile,
// ***********************************************************************
const char* sz = reader->getAttributeValueSafe("type");
Node* nd;
if (!ASSIMP_stricmp(sz,"mesh"))
if (!ASSIMP_stricmp(sz,"mesh") || !ASSIMP_stricmp(sz,"octTree"))
{
// OctTree's and meshes are treated equally
nd = new Node(Node::MESH);
}
else if (!ASSIMP_stricmp(sz,"cube"))
@ -868,7 +1045,7 @@ void IRRImporter::InternReadFile( const std::string& pFile,
else if (!ASSIMP_stricmp(sz,"skybox"))
{
nd = new Node(Node::SKYBOX);
++guessedMeshCnt;
guessedMeshCnt += 6;
}
else if (!ASSIMP_stricmp(sz,"camera"))
{
@ -901,6 +1078,16 @@ void IRRImporter::InternReadFile( const std::string& pFile,
{
nd = new Node(Node::DUMMY);
}
else if (!ASSIMP_stricmp(sz,"terrain"))
{
nd = new Node(Node::TERRAIN);
}
else if (!ASSIMP_stricmp(sz,"billBoard"))
{
// We don't support billboards, so ignore them
DefaultLogger::get()->error("IRR: Billboards are not supported by Assimp");
nd = new Node(Node::DUMMY);
}
else
{
DefaultLogger::get()->warn("IRR: Found unknown node: " + std::string(sz));
@ -929,19 +1116,21 @@ void IRRImporter::InternReadFile( const std::string& pFile,
else if (!ASSIMP_stricmp(reader->getNodeName(),"attributes"))
{
/* We should have a valid node here
* FIX: no ... the scene root node is also contained in an attributes block
*/
if (!curNode)
{
#if 0
DefaultLogger::get()->error("IRR: Encountered <attributes> element, but "
"there is no node active");
#endif
continue;
}
Animator* curAnim = NULL;
// FIX: Materials can occur for nearly any type of node
if (inMaterials /* && curNode->type == Node::ANIMMESH ||
curNode->type == Node::MESH */)
// Materials can occur for nearly any type of node
if (inMaterials && curNode->type != Node::DUMMY)
{
/* This is a material description - parse it!
*/
@ -976,10 +1165,6 @@ void IRRImporter::InternReadFile( const std::string& pFile,
VectorProperty prop;
ReadVectorProperty(prop);
// Convert to our coordinate system
std::swap( (float&)prop.value.z, (float&)prop.value.y );
prop.value.y *= -1.f;
if (inAnimator)
{
if (curAnim->type == Animator::ROTATION && prop.name == "Rotation")
@ -999,7 +1184,7 @@ void IRRImporter::InternReadFile( const std::string& pFile,
// and parse its properties
key.mValue = prop.value;
key.mTime = strtol10(&prop.name.c_str()[5]);
key.mTime = strtol10(&prop.name[5]);
}
}
else if (curAnim->type == Animator::FLY_CIRCLE)
@ -1204,6 +1389,22 @@ void IRRImporter::InternReadFile( const std::string& pFile,
}
else if (Node::LIGHT == curNode->type && "LightType" == prop.name)
{
if (prop.value == "Spot")
lights.back()->mType = aiLightSource_SPOT;
else if (prop.value == "Point")
lights.back()->mType = aiLightSource_POINT;
else if (prop.value == "Directional")
lights.back()->mType = aiLightSource_DIRECTIONAL;
else
{
// We won't pass the validation with aiLightSourceType_UNDEFINED,
// so we remove the light and replace it with a silly dummy node
delete lights.back();
lights.pop_back();
curNode->type = Node::DUMMY;
DefaultLogger::get()->error("Ignoring light of unknown type: " + prop.value);
}
}
else if (prop.name == "Mesh" && Node::MESH == curNode->type ||
Node::ANIMMESH == curNode->type)
@ -1224,9 +1425,36 @@ void IRRImporter::InternReadFile( const std::string& pFile,
aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
}
/* TODO: maybe implement the protection against recursive
* loading calls directly in BatchLoader? The current
* implementation is not absolutely safe. A LWS and an IRR
* file referencing each other *could* cause the system to
* recurse forever.
*/
std::string::size_type pos = prop.value.find_last_of('.');
// no file extension - can't read, so we don't need to try it
if( pos == std::string::npos)
{
DefaultLogger::get()->error("IRR: Can't load files without a file extension");
}
else
{
std::string extension = prop.value.substr( pos);
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
*i = ::tolower(*i);
if (".irr" == prop.value)
{
DefaultLogger::get()->error("IRR: Can't load another IRR file recursively");
}
else
{
batch.AddLoadRequest(prop.value,pp,&map);
curNode->meshPath = prop.value;
}
}
}
else if (inAnimator && prop.name == "Type")
{
// type of the animator
@ -1275,12 +1503,12 @@ void IRRImporter::InternReadFile( const std::string& pFile,
{
// currently is no node set. We need to go
// back in the node hierarchy
curParent = curParent->parent;
if (!curParent)
{
curParent = root;
DefaultLogger::get()->error("IRR: Too many closing <node> elements");
}
else curParent = curParent->parent;
}
else curNode = NULL;
}
@ -1314,6 +1542,8 @@ void IRRImporter::InternReadFile( const std::string& pFile,
else DefaultLogger::get()->warn("IRR: Camera aspect is not given, can't compute horizontal FOV");
}
batch.LoadAll();
/* Allocate a tempoary scene data structure
*/
aiScene* tempScene = new aiScene();
@ -1322,15 +1552,21 @@ void IRRImporter::InternReadFile( const std::string& pFile,
/* Copy the cameras to the output array
*/
if (!cameras.empty())
{
tempScene->mNumCameras = (unsigned int)cameras.size();
tempScene->mCameras = new aiCamera*[tempScene->mNumCameras];
::memcpy(tempScene->mCameras,&cameras[0],sizeof(void*)*tempScene->mNumCameras);
}
/* Copy the light sources to the output array
*/
if (!lights.empty())
{
tempScene->mNumLights = (unsigned int)lights.size();
tempScene->mLights = new aiLight*[tempScene->mNumLights];
::memcpy(tempScene->mLights,&lights[0],sizeof(void*)*tempScene->mNumLights);
}
// temporary data
std::vector< aiNodeAnim*> anims;
@ -1370,27 +1606,51 @@ void IRRImporter::InternReadFile( const std::string& pFile,
an->mChannels = new aiNodeAnim*[an->mNumChannels];
::memcpy(an->mChannels, & anims [0], sizeof(void*)*an->mNumChannels);
}
if (meshes.empty())
{
// There are no meshes in the scene - the scene is incomplete
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
DefaultLogger::get()->info("IRR: No Meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE flag");
}
else
if (!meshes.empty())
{
// copy all meshes to the temporary scene
tempScene->mNumMeshes = (unsigned int)meshes.size();
tempScene->mMeshes = new aiMesh*[tempScene->mNumMeshes];
::memcpy(tempScene->mMeshes,&meshes[0],tempScene->mNumMeshes);
::memcpy(tempScene->mMeshes,&meshes[0],tempScene->mNumMeshes*
sizeof(void*));
}
/* Copy all materials to the output array
*/
if (!materials.empty())
{
tempScene->mNumMaterials = (unsigned int)materials.size();
tempScene->mMaterials = new aiMaterial*[tempScene->mNumMaterials];
::memcpy(tempScene->mMaterials,&materials[0],sizeof(void*)*
tempScene->mNumMaterials);
}
/* Now merge all sub scenes and attach them to the correct
* attachment points in the scenegraph.
*/
SceneCombiner::MergeScenes(pScene,tempScene,attach);
SceneCombiner::MergeScenes(&pScene,tempScene,attach,
AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES |
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES);
/* If we have no meshes | no materials now set the INCOMPLETE
* scene flag. This is necessary if we failed to load all
* models from external files
*/
if (!pScene->mNumMeshes || !pScene->mNumMaterials)
{
DefaultLogger::get()->warn("IRR: No meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE");
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
}
// transformation matrix to convert from IRRMESH to ASSIMP coordinates
pScene->mRootNode->mTransformation *= aiMatrix4x4(1.0f, 0.0f, 0.0f, 0.f, 0.0f, 0.0f, -1.0f,
0.f, 0.0f, 1.0f, 0.0f, 0.f, 0.f, 0.f, 0.f, 1.f);
/* Finished ... everything destructs automatically and all
* temporary scenes have already been deleted by MergeScenes()
*/
return;
}

View File

@ -238,35 +238,6 @@ private:
aiVector3D position, normal, uv;
};
/** Temporary data structure to describe an IRR animator
*
* Irrlicht animations always start at the beginning, so
* we don't need "first" and "pre" for the moment.
*/
struct TemporaryAnim
{
TemporaryAnim()
: last (0)
, post (aiAnimBehaviour_DEFAULT)
, matrices (NULL)
{}
~TemporaryAnim()
{
delete[] matrices;
}
void SetupMatrices(unsigned int num)
{
last = num;
matrices = new aiMatrix4x4[num];
}
unsigned int last;
aiAnimBehaviour post;
aiMatrix4x4* matrices;
};
// -------------------------------------------------------------------
/** Fill the scenegraph recursively
@ -276,7 +247,7 @@ private:
std::vector<aiMesh*>& meshes,
std::vector<aiNodeAnim*>& anims,
std::vector<AttachmentInfo>& attach,
std::vector<aiMaterial*> materials,
std::vector<aiMaterial*>& materials,
unsigned int& defaultMatIdx);
@ -308,7 +279,7 @@ private:
* @param defMatIdx Default material index - 0xffffffff if not there
* @param mesh Mesh to work on
*/
void CopyMaterial(std::vector<aiMaterial*> materials,
void CopyMaterial(std::vector<aiMaterial*>& materials,
std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
unsigned int& defMatIdx,
aiMesh* mesh);
@ -319,16 +290,14 @@ private:
*
* @param root Node to be processed
* @param anims The list of output animations
* @param transform Transformation matrix of the current node
* (relative to the parent's coordinate space)
*/
void ComputeAnimations(Node* root, std::vector<aiNodeAnim*>& anims,
const aiMatrix4x4& transform);
void ComputeAnimations(Node* root, aiNode* real,
std::vector<aiNodeAnim*>& anims);
private:
unsigned int fps;
double fps;
};

View File

@ -238,11 +238,18 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags)
ColorFromARGBPacked(prop.value,clr);
mat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR);
}
// NOTE: The 'emissive' property causes problems. It is
// often != 0, even if there is obviously no light
// emitted by the described surface. In fact I think
// IRRLICHT ignores this property, too.
#if 0
else if (prop.name == "Emissive")
{
ColorFromARGBPacked(prop.value,clr);
mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
}
#endif
}
// Float properties
else if (!ASSIMP_stricmp(reader->getNodeName(),"float"))
@ -746,24 +753,22 @@ void IRRMeshImporter::InternReadFile( const std::string& pFile,
sz = fast_atof_move(sz,(float&)temp.x);
SkipSpaces(&sz);
sz = fast_atof_move(sz,(float&)temp.z);
SkipSpaces(&sz);
sz = fast_atof_move(sz,(float&)temp.y);
SkipSpaces(&sz);
temp.y *= -1.0f;
sz = fast_atof_move(sz,(float&)temp.z);
SkipSpaces(&sz);
curVertices.push_back(temp);
// Read the vertex normals
sz = fast_atof_move(sz,(float&)temp.x);
SkipSpaces(&sz);
sz = fast_atof_move(sz,(float&)temp.z);
SkipSpaces(&sz);
sz = fast_atof_move(sz,(float&)temp.y);
SkipSpaces(&sz);
temp.y *= -1.0f;
sz = fast_atof_move(sz,(float&)temp.z);
SkipSpaces(&sz);
curNormals.push_back(temp);
// read the vertex colors
@ -953,6 +958,10 @@ void IRRMeshImporter::InternReadFile( const std::string& pFile,
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mRootNode->mMeshes[i] = i;
// transformation matrix to convert from IRRMESH to ASSIMP coordinates
pScene->mRootNode->mTransformation *= aiMatrix4x4(
1.0f, 0.0f, 0.0f, 0.f, 0.0f, 0.0f, -1.0f, 0.f, 0.0f, 1.0f, 0.0f, 0.f, 0.f, 0.f, 0.f, 1.f);
delete reader;
AI_DEBUG_INVALIDATE_PTR(reader);
}

View File

@ -1,2 +1,97 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------------
Copyright (c) 2006-2008, 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 Implementation of the LWS importer class */
#include "AssimpPCH.h"
#include "LWSLoader.h"
#include "ParsingUtils.h"
#include "fast_atof.h"
#include "SceneCombiner.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
LWSImporter::LWSImporter()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
LWSImporter::~LWSImporter()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool LWSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
{
std::string::size_type pos = pFile.find_last_of('.');
// no file extension - can't read
if( pos == std::string::npos)
return false;
std::string extension = pFile.substr( pos);
for (std::string::iterator i = extension.begin(); i != extension.end();++i)
*i = ::tolower(*i);
return extension == ".lws";
}
// ------------------------------------------------------------------------------------------------
void LWSImporter::GetExtensionList(std::string& append)
{
append.append("*.lws");
}
// ------------------------------------------------------------------------------------------------
void LWSImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
IOSystem* pIOHandler)
{
return;
}

View File

@ -44,6 +44,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
namespace LWS {
// ---------------------------------------------------------------------------
/** Represents an element in a LWS file.
*
* This can either be a single data line - <name> <value> or it can
* be a data group - { name <data_line0> ... n }
*/
class Element
{
std::string name, data;
std::list<Element> children;
void Parse (const char* buffer);
};
} // end namespace LWS
// ---------------------------------------------------------------------------
/** LWS (LightWave Scene Format) importer class.
@ -76,10 +94,7 @@ protected:
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.lws");
}
void GetExtensionList(std::string& append);
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.

View File

@ -102,6 +102,7 @@ void OptimizeGraphProcess::FindLockedNodes(aiNode* node)
{
ai_assert(NULL != node);
// process animations
for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
{
aiAnimation* pani = pScene->mAnimations[i];
@ -110,11 +111,34 @@ void OptimizeGraphProcess::FindLockedNodes(aiNode* node)
aiNodeAnim* pba = pani->mChannels[a];
if (pba->mNodeName == node->mName)
{
// this node is locked
// this node is locked, it is referenced by an animation channel
node->mNumChildren |= AI_OG_UINT_MSB;
}
}
}
// process cameras
for (unsigned int i = 0; i < pScene->mNumCameras;++i)
{
aiCamera* p = pScene->mCameras[i];
if (p->mName == node->mName)
{
// this node is locked, it is referenced by a camera
node->mNumChildren |= AI_OG_UINT_MSB;
}
}
// process lights
for (unsigned int i = 0; i < pScene->mNumLights;++i)
{
aiLight* p = pScene->mLights[i];
if (p->mName == node->mName)
{
// this node is locked, it is referenced by a light
node->mNumChildren |= AI_OG_UINT_MSB;
}
}
// call all children
for (unsigned int i = 0; i < node->mNumChildren;++i)
FindLockedNodes(node->mChildren[i]);
@ -250,233 +274,6 @@ inline unsigned int OptimizeGraphProcess::BinarySearch(NodeIndexList& sortedArra
return (unsigned int)sortedArray.size();
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::BuildUniqueBoneList(
std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end,
std::list<BoneWithHash>& asBones)
{
unsigned int iOffset = 0;
for (; it != end;++it)
{
for (unsigned int l = 0; l < (*it)->mNumBones;++l)
{
aiBone* p = (*it)->mBones[l];
uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length);
std::list<BoneWithHash>::iterator it2 = asBones.begin();
std::list<BoneWithHash>::iterator end2 = asBones.end();
for (;it2 != end2;++it2)
{
if ((*it2).first == itml)
{
(*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
break;
}
}
if (end2 == it2)
{
// need to begin a new bone entry
asBones.push_back(BoneWithHash());
BoneWithHash& btz = asBones.back();
// setup members
btz.first = itml;
btz.second = &p->mName;
btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset));
}
}
iOffset += (*it)->mNumVertices;
}
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::JoinBones(
std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end,
aiMesh* out)
{
ai_assert(NULL != out);
// find we need to build an unique list of all bones.
// we work with hashes to make the comparisons MUCH faster,
// at least if we have many bones.
std::list<BoneWithHash> asBones;
BuildUniqueBoneList(it,end,asBones);
// now create the output bones
out->mBones = new aiBone*[asBones.size()];
for (std::list<BoneWithHash>::const_iterator it = asBones.begin(),
end = asBones.end(); it != end;++it)
{
aiBone* pc = out->mBones[out->mNumBones++] = new aiBone();
pc->mName = aiString( *((*it).second ));
// get an itrator to the end of the list
std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end();
// loop through all bones to be joined for this bone
for (std::vector< BoneSrcIndex >::const_iterator
wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
{
pc->mNumWeights += (*wmit).first->mNumWeights;
// NOTE: different offset matrices for bones with equal names
// are - at the moment - not handled correctly.
if (wmit != (*it).pSrcBones.begin() &&
pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix)
{
DefaultLogger::get()->warn("Bones with equal names but different "
"offset matrices can't be joined at the moment. If this causes "
"problems, deactivate the OptimizeGraph-Step");
continue;
}
pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
}
// allocate the vertex weight array
aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights];
// and copy the final weights - adjust the vertex IDs by the
// face index offset of the coresponding mesh.
for (std::vector< BoneSrcIndex >::const_iterator
wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
{
aiBone* pip = (*wmit).first;
for (unsigned int mp = 0; mp < pip->mNumWeights;++mp,++avw)
{
const aiVertexWeight& vfi = pip->mWeights[mp];
avw->mWeight = vfi.mWeight;
avw->mVertexId = vfi.mVertexId + (*wmit).second;
}
}
}
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::JoinMeshes(std::vector<aiMesh*>& meshList,
aiMesh*& out, unsigned int max)
{
ai_assert(NULL != out && 0 != max);
out->mMaterialIndex = meshList[0]->mMaterialIndex;
// allocate the output mesh
out = new aiMesh();
std::vector<aiMesh*>::const_iterator end = meshList.begin()+max;
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
out->mNumVertices += (*it)->mNumVertices;
out->mNumFaces += (*it)->mNumFaces;
out->mNumBones += AI_OG_UNMASK((*it)->mNumBones);
// combine primitive type flags
out->mPrimitiveTypes |= (*it)->mPrimitiveTypes;
}
if (out->mNumVertices) // just for safety
{
aiVector3D* pv2;
// copy vertex positions
if (meshList[0]->HasPositions())
{
pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D));
pv2 += (*it)->mNumVertices;
}
}
// copy normals
if (meshList[0]->HasNormals())
{
pv2 = out->mNormals = new aiVector3D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D));
pv2 += (*it)->mNumVertices;
}
}
// copy tangents and bitangents
if (meshList[0]->HasTangentsAndBitangents())
{
pv2 = out->mTangents = new aiVector3D[out->mNumVertices];
aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D));
::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D));
pv2 += (*it)->mNumVertices;
pv2b += (*it)->mNumVertices;
}
}
// copy texture coordinates
unsigned int n = 0;
while (meshList[0]->HasTextureCoords(n))
{
out->mNumUVComponents[n] = meshList[0]->mNumUVComponents[n];
pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D));
pv2 += (*it)->mNumVertices;
}
++n;
}
// copy vertex colors
n = 0;
while (meshList[0]->HasVertexColors(n))
{
aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D));
pv2 += (*it)->mNumVertices;
}
++n;
}
}
if (out->mNumFaces) // just for safety
{
// copy faces
out->mFaces = new aiFace[out->mNumFaces];
aiFace* pf2 = out->mFaces;
unsigned int ofs = 0;
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
{
for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2)
{
aiFace& face = (*it)->mFaces[m];
pf2->mNumIndices = face.mNumIndices;
pf2->mIndices = face.mIndices;
if (ofs)
{
// add the offset to the vertex
for (unsigned int q = 0; q < face.mNumIndices; ++q)
face.mIndices[q] += ofs;
}
ofs += (*it)->mNumVertices;
face.mIndices = NULL;
}
}
}
// bones - as this is quite lengthy, I moved the code to a separate function
if (out->mNumBones)JoinBones(meshList.begin(),end,out);
// delete all source meshes
for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
delete *it;
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::ApplyNodeMeshesOptimization(aiNode* pNode)
@ -513,7 +310,7 @@ void OptimizeGraphProcess::ApplyNodeMeshesOptimization(aiNode* pNode)
if (iNumMeshes > 0)
{
apcMeshes[iNumMeshes++] = pScene->mMeshes[nm];
JoinMeshes(apcMeshes,out,iNumMeshes);
// JoinMeshes(apcMeshes,out,iNumMeshes);
}
else out = pScene->mMeshes[nm];
@ -785,13 +582,13 @@ void OptimizeGraphProcess::Execute( aiScene* pScene)
a) the term "mesh node" stands for a node with numMeshes > 0
b) the term "animation node" stands for a node with numMeshes == 0,
regardless whether the node is referenced by animation channels.
regardless whether the node is referenced by animation channels,
lights or cameras
Algorithm:
1. Compute hashes for all meshes that we're able to check whether
two meshes are compatible.
2. Remove animation nodes if we have been configured to do so
3. Find out which nodes may not be moved, so to speak are "locked" - a
locked node will never be joined with neighbors.
- A node lock is indicated by a set MSB in the aiNode::mNumChildren member

View File

@ -87,14 +87,6 @@ struct NodeIndexEntry : public std::pair<unsigned int, unsigned int>
aiNode* pNode;
};
typedef std::vector<NodeIndexEntry> NodeIndexList;
typedef std::pair<aiBone*,unsigned int> BoneSrcIndex;
// ---------------------------------------------------------------------------
struct BoneWithHash : public std::pair<uint32_t,aiString*>
{
std::vector<BoneSrcIndex> pSrcBones;
};
// ---------------------------------------------------------------------------
/** This post processing step reformats the output node graph to be more
@ -230,38 +222,6 @@ protected:
void ApplyNodeMeshesOptimization(aiNode* pNode);
// -------------------------------------------------------------------
/** Join meshes.
* The output meshes are deleted afterwards.
* @param meshList List of meshes to be joined
* @param out Receives a pointer to the output mesh.
*/
void JoinMeshes(std::vector<aiMesh*>& meshList,aiMesh*& out,
unsigned int max);
// -------------------------------------------------------------------
/** Join bones from a collection of meshes.
*
* @param it First mesh to be processed
* @param end Last mesh to be processed
* @param out Valid output mesh to receive the output bone list.
*/
void JoinBones(std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end,
aiMesh* out);
// -------------------------------------------------------------------
/** Build a list of unique bones from a collection of meshes.
*
* @param it First mesh to be processed
* @param end Last mesh to be processed
* @param asBones Receives a list of unique bones
*/
void BuildUniqueBoneList(std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end,
std::list<BoneWithHash>& asBones);
// -------------------------------------------------------------------
/** Build the output mesh list.

View File

@ -48,14 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// MSB for type unsigned int
#define AI_RC_UINT_MSB (1u<<((sizeof(unsigned int)<<3u)-1u))
#define AI_RC_UINT_MSB_2 (AI_RC_UINT_MSB>>1u)
// unmask the two upper bits of an unsigned int
#define AI_RC_UNMASK(p) (p & (~(AI_RC_UINT_MSB|AI_RC_UINT_MSB_2)))
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
RemoveVCProcess::RemoveVCProcess()
@ -79,89 +71,70 @@ bool RemoveVCProcess::IsActive( unsigned int pFlags) const
// ------------------------------------------------------------------------------------------------
// Small helper function to delete all elements in a T** aray using delete
template <typename T>
inline void ArrayDelete(T**& in, unsigned int num)
inline void ArrayDelete(T**& in, unsigned int& num)
{
for (unsigned int i = 0; i < num; ++i)
delete in[i];
delete[] in;
in = NULL;
num = 0;
}
// ------------------------------------------------------------------------------------------------
// Small helper function to set a sepcific bit in the aiNode::mNumMeshes member of all nodes
// that are referenced by the elements of an array (T::mName must be there)
template <typename T>
inline void MaskNodes(aiNode* node,T** in, unsigned int num, unsigned int bit)
{
if (node->mName.length)
{
for (unsigned int i = 0; i < num;++i)
{
T* cur = in[i];
if (cur->mName == node->mName)
{
node->mNumMeshes |= bit;
break;
}
}
}
for (unsigned int i = 0; i < node->mNumChildren;++i)
MaskNodes(node->mChildren[i],in,num,bit);
}
// ------------------------------------------------------------------------------------------------
// Updates the node graph - removes all nodes which have the "remove" flag set and the
// "don't remove" flag not set. Nodes with meshes are never deleted.
bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
{
register bool b = false;
std::list<aiNode*> mine;
for (unsigned int i = 0; i < node->mNumChildren;++i)
{
if(UpdateNodeGraph(node->mChildren[i],mine,false))
b = true;
}
if (!root && !node->mNumMeshes && AI_RC_UINT_MSB == (node->mNumMeshes & (AI_RC_UINT_MSB | AI_RC_UINT_MSB_2)))
{
// this node needs to be removed
if(node->mNumChildren)
{
childsOfParent.insert(childsOfParent.end(),mine.begin(),mine.end());
// set all children to NULL to make sure they are not deleted when we delete ourself
for (unsigned int i = 0; i < node->mNumChildren;++i)
node->mChildren[i] = NULL;
}
b = true;
delete node;
}
else
{
AI_RC_UNMASK(node->mNumMeshes);
childsOfParent.push_back(node);
if (b)
{
// reallocate the array of our children here
node->mNumChildren = (unsigned int)mine.size();
aiNode** const children = new aiNode*[mine.size()];
aiNode** ptr = children;
for (std::list<aiNode*>::iterator it = mine.begin(), end = mine.end();
it != end; ++it)
{
*ptr++ = *it;
}
delete[] node->mChildren;
node->mChildren = children;
return false;
}
}
return b;
}
//// ------------------------------------------------------------------------------------------------
//// Updates the node graph - removes all nodes which have the "remove" flag set and the
//// "don't remove" flag not set. Nodes with meshes are never deleted.
//bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
//{
// register bool b = false;
//
// std::list<aiNode*> mine;
// for (unsigned int i = 0; i < node->mNumChildren;++i)
// {
// if(UpdateNodeGraph(node->mChildren[i],mine,false))
// b = true;
// }
//
// // somewhat tricky ... mNumMeshes must be originally 0 and MSB2 may not be set,
// // so we can do a simple comparison against MSB here
// if (!root && AI_RC_UINT_MSB == node->mNumMeshes )
// {
// // this node needs to be removed
// if(node->mNumChildren)
// {
// childsOfParent.insert(childsOfParent.end(),mine.begin(),mine.end());
//
// // set all children to NULL to make sure they are not deleted when we delete ourself
// for (unsigned int i = 0; i < node->mNumChildren;++i)
// node->mChildren[i] = NULL;
// }
// b = true;
// delete node;
// }
// else
// {
// AI_RC_UNMASK(node->mNumMeshes);
// childsOfParent.push_back(node);
//
// if (b)
// {
// // reallocate the array of our children here
// node->mNumChildren = (unsigned int)mine.size();
// aiNode** const children = new aiNode*[mine.size()];
// aiNode** ptr = children;
//
// for (std::list<aiNode*>::iterator it = mine.begin(), end = mine.end();
// it != end; ++it)
// {
// *ptr++ = *it;
// }
// delete[] node->mChildren;
// node->mChildren = children;
// return false;
// }
// }
// return b;
//}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
@ -175,6 +148,7 @@ void RemoveVCProcess::Execute( aiScene* pScene)
// handle animations
if ( configDeleteFlags & aiComponent_ANIMATIONS)
{
bHas = true;
ArrayDelete(pScene->mAnimations,pScene->mNumAnimations);
}
@ -212,21 +186,13 @@ void RemoveVCProcess::Execute( aiScene* pScene)
// handle light sources
if ( configDeleteFlags & aiComponent_LIGHTS)
{
// mask nodes for removal
MaskNodes(pScene->mRootNode,pScene->mLights,pScene->mNumLights,
AI_RC_UINT_MSB);
bHas = bMasked = true;
bHas = true;
ArrayDelete(pScene->mLights,pScene->mNumLights);
}
// handle camneras
if ( configDeleteFlags & aiComponent_CAMERAS)
{
// mask nodes for removal
MaskNodes(pScene->mRootNode,pScene->mLights,pScene->mNumLights,
AI_RC_UINT_MSB);
bHas = true;
ArrayDelete(pScene->mCameras,pScene->mNumCameras);
}
@ -241,46 +207,11 @@ void RemoveVCProcess::Execute( aiScene* pScene)
{
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{
if( this->ProcessMesh( pScene->mMeshes[a]))
if( ProcessMesh( pScene->mMeshes[a]))
bHas = true;
}
if (configDeleteFlags & aiComponent_BONEWEIGHTS && bHas)bMasked = true;
}
// now check which scenegraph nodes are unnecessary now
// we use the upper two bits of aiNode::mNumMeshes as
// temporary storage.
// MSB means: REMOVE ME!
// MSB>>1 means: NO, DON'T REMOVE ME (Veto)
if (bMasked)
{
if (pScene->mNumLights)
{
MaskNodes(pScene->mRootNode,pScene->mLights,pScene->mNumLights,
AI_RC_UINT_MSB_2);
}
if (pScene->mNumCameras)
{
MaskNodes(pScene->mRootNode,pScene->mCameras,pScene->mNumCameras,
AI_RC_UINT_MSB_2);
}
if (!(configDeleteFlags & aiComponent_BONEWEIGHTS))
{
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
aiMesh* mesh = pScene->mMeshes[i];
if (mesh->mNumBones)
{
MaskNodes(pScene->mRootNode,mesh->mBones,mesh->mNumBones,
AI_RC_UINT_MSB_2);
}
}
}
std::list<aiNode*> dummy;
UpdateNodeGraph(pScene->mRootNode,dummy, true);
// the root node will never be deleted
}
// now check whether the result is still a full scene
if (!pScene->mNumMeshes || !pScene->mNumMaterials)
@ -385,13 +316,8 @@ bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh)
// handle bones
if (configDeleteFlags & aiComponent_BONEWEIGHTS && pMesh->mBones)
{
// mask nodes for removal
MaskNodes(mScene->mRootNode,pMesh->mBones,pMesh->mNumBones,
AI_RC_UINT_MSB);
ArrayDelete(pMesh->mBones,pMesh->mNumBones);
ret = true;
}
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@ -69,6 +69,43 @@ struct AttachmentInfo
aiNode* attachToNode;
};
// ---------------------------------------------------------------------------
struct NodeAttachmentInfo
{
NodeAttachmentInfo()
: node (NULL)
, attachToNode (NULL)
{}
NodeAttachmentInfo(aiNode* _scene, aiNode* _attachToNode)
: node (_scene)
, attachToNode (_attachToNode)
{}
aiNode* node;
aiNode* attachToNode;
};
// generate unique names for all named scene items
#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES 0x1
// generate unique names for materials, too
#define AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES 0x2
// use deep copies of duplicate scenes
#define AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY 0x4
typedef std::pair<aiBone*,unsigned int> BoneSrcIndex;
// ---------------------------------------------------------------------------
/** \brief Helper data structure for SceneCombiner::MergeBones.
*
*/
struct BoneWithHash : public std::pair<uint32_t,aiString*>
{
std::vector<BoneSrcIndex> pSrcBones;
};
// ---------------------------------------------------------------------------
/** \brief Static helper class providing various utilities to merge two
* scenes. It is intended as internal utility and NOT for use by
@ -87,12 +124,14 @@ public:
// -------------------------------------------------------------------
/** Merges two or more scenes.
*
* @param dest Destination scene. Must be empty.
* @param dest Receives a pointer to the destination scene. If the
* pointer doesn't point to NULL when the function is called, the
* existing scene is cleared and refilled.
* @param src Non-empty list of scenes to be merged. The function
* deletes the input scenes afterwards.
* deletes the input scenes afterwards. There may be duplicate scenes.
* @param flags Combination of the AI_INT_MERGE_SCENE flags defined above
*/
static void MergeScenes(aiScene* dest,std::vector<aiScene*>& src,
static void MergeScenes(aiScene** dest,std::vector<aiScene*>& src,
unsigned int flags = 0);
@ -100,31 +139,63 @@ public:
/** Merges two or more scenes and attaches all sceenes to a specific
* position in the node graph of the masteer scene.
*
* @param dest Destination scene. Must be empty.
* @param dest Receives a pointer to the destination scene. If the
* pointer doesn't point to NULL when the function is called, the
* existing scene is cleared and refilled.
* @param master Master scene. It will be deleted afterwards. All
* other scenes will be inserted in its node graph.
* @param src Non-empty list of scenes to be merged along with their
* corresponding attachment points in the master scene. The function
* deletes the input scenes afterwards.
* deletes the input scenes afterwards. There may be duplicate scenes.
* @param flags Combination of the AI_INT_MERGE_SCENE flags defined above
*/
static void MergeScenes(aiScene* dest, const aiScene* master,
static void MergeScenes(aiScene** dest, aiScene* master,
std::vector<AttachmentInfo>& src,
unsigned int flags = 0);
// -------------------------------------------------------------------
/** Merges two or more meshes
*
* The meshes should have equal vertex formats. Only components
* that are provided by ALL meshes will be present in the output mesh.
* An exception is made for VColors - they are set to black. The
* meshes should have the same material indices, too. The output
* material index is always the material index of the first mesh.
*
* @param dest Destination mesh. Must be empty.
* @param src Non-empty list of meshes to be merged. The function
* deletes the input meshes afterwards.
* @param flags Currently no parameters
* @param begin First mesh to be processed
* @param end Points to the mesh after the last mesh to be processed
*/
static void MergeMeshes(aiMesh* dest,std::vector<aiMesh*>& src,
unsigned int flags);
static void MergeMeshes(aiMesh** dest,unsigned int flags,
std::vector<aiMesh*>::const_iterator begin,
std::vector<aiMesh*>::const_iterator end);
// -------------------------------------------------------------------
/** Merges two or more bones
*
* @param out Mesh to receive the output bone list
* @param flags Currently no parameters
* @param begin First mesh to be processed
* @param end Points to the mesh after the last mesh to be processed
*/
static void MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end);
// -------------------------------------------------------------------
/** Builds a list of uniquely named bones in a mesh list
*
* @param asBones Receives the output list
* @param it First mesh to be processed
* @param end Last mesh to be processed
*/
static void BuildUniqueBoneList(std::list<BoneWithHash>& asBones,
std::vector<aiMesh*>::const_iterator it,
std::vector<aiMesh*>::const_iterator end);
// -------------------------------------------------------------------
/** Add a name prefix to all nodes in a scene.
*
@ -134,6 +205,75 @@ public:
*/
static void AddNodePrefixes(aiNode* node, const char* prefix,
unsigned int len);
// -------------------------------------------------------------------
/** Add an offset to all mesh indices in a node graph
*
* @param Current node. This function is called recursively.
* @param offset Offset to be added to all mesh indices
*/
static void OffsetNodeMeshIndices (aiNode* node, unsigned int offset);
// -------------------------------------------------------------------
/** Attach a list of node graphs to well-defined nodes in a master
* graph. This is a helper for MergeScenes()
*
* @param master Master scene
* @param srcList List of source scenes along with their attachment
* points. If an attachment point is NULL (or does not exist in
* the master graph), a scene is attached to the root of the master
* graph (as an additional child node)
* @duplicates List of duplicates. If elem[n] == n the scene is not
* a duplicate. Otherwise elem[n] links scene n to its first occurence.
*/
static void AttachToGraph ( aiScene* master,
std::vector<NodeAttachmentInfo>& srcList);
static void AttachToGraph (aiNode* attach,
std::vector<NodeAttachmentInfo>& srcList);
// -------------------------------------------------------------------
/** Get a deep copy of a scene
*
* @param dest Receives a pointer to the destination scene
* @param src Source scene - remains unmodified.
*/
static void CopyScene(aiScene** dest,aiScene* source);
// -------------------------------------------------------------------
/** Get a flat copy of a scene
*
* Only the first hierarchy layer is copied. All pointer members of
* aiScene are shared by source and destination scene. If the
* pointer doesn't point to NULL when the function is called, the
* existing scene is cleared and refilled.
* @param dest Receives a pointer to the destination scene
* @param src Source scene - remains unmodified.
*/
static void CopySceneFlat(aiScene** dest,aiScene* source);
// -------------------------------------------------------------------
/** Get a deep copy of a mesh
*
* @param dest Receives a pointer to the destination mesh
* @param src Source mesh - remains unmodified.
*/
static void Copy (aiMesh** dest, const aiMesh* src);
// similar to Copy():
static void Copy (aiMaterial** dest, const aiMaterial* src);
static void Copy (aiTexture** dest, const aiTexture* src);
static void Copy (aiAnimation** dest, const aiAnimation* src);
static void Copy (aiCamera** dest, const aiCamera* src);
static void Copy (aiBone** dest, const aiBone* src);
static void Copy (aiLight** dest, const aiLight* src);
static void Copy (aiNodeAnim** dest, const aiNodeAnim* src);
// recursive, of course
static void Copy (aiNode** dest, const aiNode* src);
};
}

View File

@ -194,6 +194,6 @@ void ScenePreprocessor::ProcessAnimation (aiAnimation* anim)
if (anim->mDuration == -1.)
{
DefaultLogger::get()->debug("Setting animation duration");
anim->mDuration = last - first;
anim->mDuration = last - std::min( first, 0. );
}
}

View File

@ -44,7 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ---------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
KeyIterator::KeyIterator(const std::vector<aiVectorKey>* _objPos,
const std::vector<aiVectorKey>* _targetObjPos,
const aiVector3D* defaultObjectPos /*= NULL*/,
@ -80,7 +80,14 @@ KeyIterator::KeyIterator(const std::vector<aiVectorKey>* _objPos,
}
}
// ---------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
template <class T>
T Interpolate(const T& one, const T& two, float val)
{
return one + (two-one)*val;
}
// ------------------------------------------------------------------------------------------------
void KeyIterator::operator ++()
{
// If we are already at the end of all keyframes, return
@ -129,8 +136,8 @@ void KeyIterator::operator ++()
const aiVectorKey& last = targetObjPos->at(nextTargetObjPos);
const aiVectorKey& first = targetObjPos->at(nextTargetObjPos-1);
/* curTargetPosition = Interpolate(first.mValue, last.mValue,
(curTime-first.mTime) / (last.mTime-first.mTime));*/
curTargetPosition = Interpolate(first.mValue, last.mValue, (float) (
(curTime-first.mTime) / (last.mTime-first.mTime) ));
}
// increment counters
@ -155,8 +162,8 @@ void KeyIterator::operator ++()
const aiVectorKey& last = objPos->at(nextObjPos);
const aiVectorKey& first = objPos->at(nextObjPos-1);
/*curPosition = Interpolate(first.mValue, last.mValue,
(curTime-first.mTime) / (last.mTime-first.mTime));*/
curPosition = Interpolate(first.mValue, last.mValue, (float) (
(curTime-first.mTime) / (last.mTime-first.mTime)));
}
}
@ -167,7 +174,7 @@ void KeyIterator::operator ++()
}
}
// ---------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::SetTargetAnimationChannel (
const std::vector<aiVectorKey>* _targetPositions)
{
@ -175,7 +182,7 @@ void TargetAnimationHelper::SetTargetAnimationChannel (
targetPositions = _targetPositions;
}
// ---------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::SetMainAnimationChannel (
const std::vector<aiVectorKey>* _objectPositions)
{
@ -183,7 +190,7 @@ void TargetAnimationHelper::SetMainAnimationChannel (
objectPositions = _objectPositions;
}
// ---------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::SetFixedMainAnimationChannel(
const aiVector3D& fixed)
{
@ -191,23 +198,53 @@ void TargetAnimationHelper::SetFixedMainAnimationChannel(
fixedMain = fixed;
}
// ---------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::Process(std::vector<aiVectorKey>* distanceTrack)
{
ai_assert(NULL != targetPositions);
// TODO: in most cases we won't need the extra array
std::vector<aiVectorKey>* fill = NULL;
std::vector<aiVectorKey> real;
if (distanceTrack)
{
fill = (distanceTrack == objectPositions ? &real : distanceTrack);
}
fill->reserve(std::max( objectPositions->size(), targetPositions->size() ));
// Iterate through all object keys and interpolate their values if necessary.
// Then get the corresponding target position, compute the difference
// vector between object and target position. Then compute a rotation matrix
// that rotates the base vector of the object coordinate system at that time
// to match the diff vector.
KeyIterator iter(objectPositions,targetPositions,&fixedMain);
unsigned int curTarget;
for (;!iter.Finished();++iter)
{
const aiVector3D& position = iter.GetCurPosition();
const aiVector3D& tposition = iter.GetCurTargetPosition();
// diff vector
aiVector3D diff = tposition - position;
float f = diff.SquareLength();
if (!f)
{
DefaultLogger::get()->error("Target position equals object position");
continue;
}
f = ::sqrt(f);
// output distance vector
if (fill)
{
fill->push_back(aiVectorKey());
aiVectorKey& v = fill->back();
v.mTime = iter.GetCurTime();
v.mValue = aiVector3D (0.f,0.f,f);
}
diff /= f;
}
if (real.size())
*distanceTrack = real;
}

View File

@ -98,6 +98,9 @@ public:
inline const aiVector3D& GetCurTargetPosition() const
{return curTargetPosition;}
inline double GetCurTime() const
{return curTime;}
private:
//! Did we reach the end?

View File

@ -114,22 +114,20 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
return;
}
float absTranslationX = info.mScaling.x * info.mTranslation.x;
float absTranslationY = info.mScaling.y * info.mTranslation.y;
/* Optimize UV translation in the U direction. To determine whether
* or not we can optimize we need to look at the requested mapping
* type (e.g. if mirroring is active there IS a difference between
* offset 2 and 3)
*/
if ((rounded = (int)absTranslationX))
if ((rounded = (int)info.mTranslation.x))
{
float out;
szTemp[0] = 0;
if (aiTextureMapMode_Wrap == info.mapU)
{
// Wrap - simple take the fraction of the field
out = (absTranslationX-(float)rounded) / info.mScaling.x;
out = info.mTranslation.x-(float)rounded;
sprintf(szTemp,"[w] UV U offset %f can "
"be simplified to %f",info.mTranslation.x,out);
}
@ -137,7 +135,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
{
// Mirror
if (rounded % 2)rounded--;
out = (absTranslationX-(float)rounded) / info.mScaling.x;
out = info.mTranslation.x-(float)rounded;
sprintf(szTemp,"[m/d] UV U offset %f can "
"be simplified to %f",info.mTranslation.x,out);
@ -162,14 +160,14 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
* type (e.g. if mirroring is active there IS a difference between
* offset 2 and 3)
*/
if ((rounded = (int)absTranslationY))
if ((rounded = (int)info.mTranslation.y))
{
float out;
szTemp[0] = 0;
if (aiTextureMapMode_Wrap == info.mapV)
{
// Wrap - simple take the fraction of the field
out = (absTranslationY-(float)rounded) / info.mScaling.y;
out = info.mTranslation.y-(float)rounded;
sprintf(szTemp,"[w] UV V offset %f can "
"be simplified to %f",info.mTranslation.y,out);
}
@ -177,7 +175,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
{
// Mirror
if (rounded % 2)rounded--;
out = (absTranslationY-(float)rounded) / info.mScaling.y;
out = info.mTranslation.x-(float)rounded;
sprintf(szTemp,"[m/d] UV V offset %f can "
"be simplified to %f",info.mTranslation.y,out);
@ -272,7 +270,7 @@ void TextureTransformStep::Execute( aiScene* pScene)
info.uvIndex = *((int*)prop2->mData);
// Store a direct pointer for later use
update.directShortcut = (unsigned int*) &prop2->mData;
update.directShortcut = (unsigned int*) prop2->mData;
}
else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodeu"))
@ -286,8 +284,7 @@ void TextureTransformStep::Execute( aiScene* pScene)
// ValidateDS should check this
ai_assert(prop2->mDataLength >= 20);
::memcpy(&info.mTranslation.x,prop2->mData,
sizeof(float)*5);
::memcpy(&info.mTranslation.x,prop2->mData,sizeof(float)*5);
delete[] prop2->mData;

View File

@ -114,7 +114,7 @@ struct STransformVecInfo : public aiUVTransform
inline bool operator== (const STransformVecInfo& other) const
{
// We use a small epsilon here
const float epsilon = 0.05f;
const static float epsilon = 0.05f;
if (fabs( mTranslation.x - other.mTranslation.x ) > epsilon ||
fabs( mTranslation.y - other.mTranslation.y ) > epsilon)

View File

@ -637,8 +637,6 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
szType,iIndex,iNumIndices,szType);
}
if (!iNumIndices)return;
// TODO: check whether the mappings are correctly
std::vector<aiTextureMapping> mappings(iNumIndices);
// Now check whether all UV indices are valid ...
@ -711,7 +709,7 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
for (unsigned int a = 0; a < mScene->mNumMeshes;++a)
{
aiMesh* mesh = mScene->mMeshes[a];
if(mesh->mMaterialIndex == (unsigned int)iIndex)
if(mesh->mMaterialIndex == (unsigned int)iIndex && mappings[0] == aiTextureMapping_UV)
{
if (!mesh->mTextureCoords[0])
{

View File

@ -0,0 +1,36 @@
#ifndef BOOST_MATH_COMMON_FACTOR_RT_HPP
#define BOOST_MATH_COMMON_FACTOR_RT_HPP
namespace boost {
namespace math {
// TODO: use binary GCD for unsigned integers ....
template < typename IntegerType >
IntegerType gcd( IntegerType const &a, IntegerType const &b )
{
while ( true )
{
if ( a == zero )
return b;
b %= a;
if ( b == zero )
return a;
a %= b;
}
}
template < typename IntegerType >
IntegerType lcm( IntegerType const &a, IntegerType const &b )
{
IntegerType t = gcd (a,b);
if (!t)return t;
return a / t * b;
}
}}
#endif

View File

@ -51,8 +51,8 @@ public:
virtual std::string getOsSeparator() const = 0;
// -------------------------------------------------------------------
/** Open a new file with a given path. When the access to the file is finished,
* call Close() to release all associated resources.
/** Open a new file with a given path. When the access to the file
* is finished, call Close() to release all associated resources.
*
* @param pFile Path to the file
* @param pMode Desired file I/O mode. Required are: "wb", "w", "wt",
@ -60,8 +60,8 @@ public:
*
* @return New IOStream interface allowing the lib to access
* the underlying file.
* @note When implementing this class to provide custom IO handling, you propably
* have to supply an own implementation of IOStream as well.
* @note When implementing this class to provide custom IO handling,
* you propably have to supply an own implementation of IOStream as well.
*/
virtual IOStream* Open(
const std::string& pFile,
@ -72,6 +72,20 @@ public:
* @param pFile The file instance previously created by Open().
*/
virtual void Close( IOStream* pFile) = 0;
// -------------------------------------------------------------------
/** Compares two paths and check whether the point to identical files.
*
* The dummy implementation of this virtual performs a
* case-insensitive comparison of the path strings.
* @param one First file
* @param second Second file
* @return true if the paths point to the same file. The file needn't
* be existing, however.
*/
virtual bool ComparePaths (const std::string& one,
const std::string& second);
};
// ----------------------------------------------------------------------------
@ -87,6 +101,7 @@ inline IOSystem::~IOSystem()
}
// ----------------------------------------------------------------------------
} //!ns Assimp
#endif //AI_IOSYSTEM_H_INC

View File

@ -191,6 +191,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Use our own definition of PI here
#define AI_MATH_PI (3.1415926538)
#define AI_MATH_TWO_PI (AI_MATH_PI * 2.0)
#define AI_MATH_HALF_PI (AI_MATH_PI * 0.5)
// macrod to convert from radians to degrees and the reverse
#define AI_DEG_TO_RAD(x) (x*0.0174532925f)

View File

@ -119,6 +119,7 @@ struct aiMatrix4x4
* \param z Rotation angle for the z-axis, in radians
*/
inline void FromEulerAngles(float x, float y, float z);
inline void FromEulerAngles(const aiVector3D& blubb);
/** \brief Returns a rotation matrix for a rotation around the x axis

View File

@ -87,15 +87,12 @@ inline aiMatrix4x4& aiMatrix4x4::Inverse()
float det = Determinant();
if(det == 0.0f)
{
const float nan = std::numeric_limits<float>::quiet_NaN();
*this = aiMatrix4x4(
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(),std::numeric_limits<float>::quiet_NaN());
nan,nan,nan,nan,
nan,nan,nan,nan,
nan,nan,nan,nan,
nan,nan,nan,nan);
return *this;
}
@ -129,7 +126,6 @@ inline float* aiMatrix4x4::operator[](unsigned int p_iIndex)
return &this->a1 + p_iIndex * 4;
}
// ---------------------------------------------------------------------------
inline const float* aiMatrix4x4::operator[](unsigned int p_iIndex) const
{
@ -208,6 +204,11 @@ inline void aiMatrix4x4::DecomposeNoScaling (aiQuaternion& rotation,
rotation = aiQuaternion((aiMatrix3x3)_this);
}
// ---------------------------------------------------------------------------
inline void aiMatrix4x4::FromEulerAngles(const aiVector3D& blubb)
{
FromEulerAngles(blubb.x,blubb.y,blubb.z);
}
// ---------------------------------------------------------------------------
inline void aiMatrix4x4::FromEulerAngles(float x, float y, float z)
{
aiMatrix4x4& _this = *this;

View File

@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// include math helper classes
#include "aiVector3D.h"
#include "aiVector2D.h"
#include "aiMatrix3x3.h"
#include "aiMatrix4x4.h"
@ -66,22 +67,6 @@ const size_t MAXLEN = 1024;
#include "./Compiler/pushpack1.h"
// ---------------------------------------------------------------------------
/** Represents a two-dimensional vector.
*/
struct aiVector2D
{
#ifdef __cplusplus
aiVector2D () : x(0.0f), y(0.0f) {}
aiVector2D (float _x, float _y) : x(_x), y(_y) {}
aiVector2D (const aiVector2D& o) : x(o.x), y(o.y) {}
#endif // !__cplusplus
//! X and y coordinates
float x, y;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** Represents a plane in a three-dimensional, euclidean space
@ -101,6 +86,25 @@ struct aiPlane
float a,b,c,d;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** Represents a ray
*/
struct aiRay
{
#ifdef __cplusplus
aiRay () {}
aiRay (const aiVector3D& _pos, const aiVector3D& _dir)
: pos(_pos), dir(_dir) {}
aiRay (const aiRay& o) : pos (o.pos), dir (o.dir) {}
#endif // !__cplusplus
//! Position and direction of the ray
aiVector3D pos, dir;
} PACK_STRUCT;
// aiVector3D type moved to separate header due to size of operators
// aiQuaternion type moved to separate header due to size of operators
// aiMatrix4x4 type moved to separate header due to size of operators

View File

@ -0,0 +1,148 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------------
Copyright (c) 2006-2008, 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 2D vector structure, including operators when compiling in C++ */
#ifndef AI_VECTOR2D_H_INC
#define AI_VECTOR2D_H_INC
#include <math.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "./Compiler/pushpack1.h"
// ---------------------------------------------------------------------------
/** Represents a two-dimensional vector. */
struct aiVector2D
{
#ifdef __cplusplus
aiVector2D () : x(0.0f), y(0.0f) {}
aiVector2D (float _x, float _y) : x(_x), y(_y) {}
aiVector2D (float _xyz) : x(_xyz), y(_xyz) {}
aiVector2D (const aiVector2D& o) : x(o.x), y(o.y) {}
void Set( float pX, float pY) { x = pX; y = pY;}
float SquareLength() const { return x*x + y*y; }
float Length() const { return sqrt( SquareLength()); }
aiVector2D& Normalize() { *this /= Length(); return *this; }
const aiVector2D& operator += (const aiVector2D& o) { x += o.x; y += o.y; return *this; }
const aiVector2D& operator -= (const aiVector2D& o) { x -= o.x; y -= o.y; return *this; }
const aiVector2D& operator *= (float f) { x *= f; y *= f; return *this; }
const aiVector2D& operator /= (float f) { x /= f; y /= f; return *this; }
inline float operator[](unsigned int i) const {return *(&x + i);}
inline float& operator[](unsigned int i) {return *(&x + i);}
inline bool operator== (const aiVector2D& other) const
{return x == other.x && y == other.y;}
inline bool operator!= (const aiVector2D& other) const
{return x != other.x || y != other.y;}
inline aiVector2D& operator= (float f)
{x = y = f;return *this;}
const aiVector2D SymMul(const aiVector2D& o)
{return aiVector2D(x*o.x,y*o.y);}
#endif // __cplusplus
float x, y;
} PACK_STRUCT;
#include "./Compiler/poppack1.h"
#ifdef __cplusplus
} // end extern "C"
// symmetric addition
inline aiVector2D operator + (const aiVector2D& v1, const aiVector2D& v2)
{
return aiVector2D( v1.x + v2.x, v1.y + v2.y);
}
// symmetric subtraction
inline aiVector2D operator - (const aiVector2D& v1, const aiVector2D& v2)
{
return aiVector2D( v1.x - v2.x, v1.y - v2.y);
}
// scalar product
inline float operator * (const aiVector2D& v1, const aiVector2D& v2)
{
return v1.x*v2.x + v1.y*v2.y;
}
// scalar multiplication
inline aiVector2D operator * ( float f, const aiVector2D& v)
{
return aiVector2D( f*v.x, f*v.y);
}
// and the other way around
inline aiVector2D operator * ( const aiVector2D& v, float f)
{
return aiVector2D( f*v.x, f*v.y);
}
// scalar division
inline aiVector2D operator / ( const aiVector2D& v, float f)
{
return v * (1/f);
}
// vector division
inline aiVector2D operator / ( const aiVector2D& v, const aiVector2D& v2)
{
return aiVector2D(v.x / v2.x,v.y / v2.y);
}
// vector inversion
inline aiVector2D operator - ( const aiVector2D& v)
{
return aiVector2D( -v.x, -v.y);
}
#endif // __cplusplus
#endif // AI_VECTOR2D_H_INC

View File

@ -9,7 +9,7 @@ kids 0
OBJECT poly
name "sphere"
loc -0.0624103 -0.012381 0.0558408
texture "./../LWOFiles/LWO2/MappingModes/concrete.jpg"
texture "./../LWOFiles/LWO2/MappingModes/earthSpherical.jpg"
crease 45.000000
numvert 134
-0.00202139 0.0563461 0

View File

@ -9,7 +9,7 @@ kids 0
OBJECT poly
name "sphere"
loc -0.0624103 -0.012381 0.0558408
texture "./../LWOFiles/LWO2/MappingModes/concrete.jpg"
texture "./../LWOFiles/LWO2/MappingModes/earthSpherical.jpg"
texrep 1.9 2.5
texoff 0.019 0.5
crease 45.000000

View File

@ -663,6 +663,10 @@
RelativePath="..\..\include\aiTypes.h"
>
</File>
<File
RelativePath="..\..\include\aiVector2D.h"
>
</File>
<File
RelativePath="..\..\include\aiVector3D.h"
>
@ -722,6 +726,10 @@
<Filter
Name="BoostWorkaround"
>
<File
RelativePath="..\..\include\BoostWorkaround\boost\common_factor_rt.hpp"
>
</File>
<File
RelativePath="..\..\include\BoostWorkaround\boost\format.hpp"
>