blender loader now reads basic material colors and even simple textures, be them embedded into the BLEND file or not.

adding some test files.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@735 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2010-05-24 01:24:18 +00:00
parent 2a6b4d1d17
commit 188aa3da37
10 changed files with 338 additions and 24 deletions

View File

@ -111,6 +111,15 @@ struct Pointer
uint64_t val; uint64_t val;
}; };
// -------------------------------------------------------------------------------
/** Represents a generic offset within a BLEND file */
// -------------------------------------------------------------------------------
struct FileOffset
{
FileOffset() : val() {}
uint64_t val;
};
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
/** Dummy derivate of std::vector to be able to use it in templates simultaenously /** Dummy derivate of std::vector to be able to use it in templates simultaenously
* with boost::shared_ptr, which takes only one template argument * with boost::shared_ptr, which takes only one template argument
@ -263,6 +272,13 @@ public:
void ReadFieldPtr(TOUT<T>& out, const char* name, void ReadFieldPtr(TOUT<T>& out, const char* name,
const FileDatabase& db) const; const FileDatabase& db) const;
// --------------------------------------------------------
// field parsing for static arrays of pointer or dynamic
// array types (boost::shared_ptr[] or boost::shared_array[])
template <int error_policy, template <typename> class TOUT, typename T, size_t N>
void ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
const FileDatabase& db) const;
// -------------------------------------------------------- // --------------------------------------------------------
// field parsing for `normal` values // field parsing for `normal` values
template <int error_policy, typename T> template <int error_policy, typename T>
@ -281,6 +297,10 @@ private:
void ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval, void ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval,
const FileDatabase& db, const Field& f) const; const FileDatabase& db, const Field& f) const;
// --------------------------------------------------------
void ResolvePointer( boost::shared_ptr< FileOffset >& out, const Pointer & ptrval,
const FileDatabase& db, const Field& f) const;
// -------------------------------------------------------- // --------------------------------------------------------
inline const FileBlockHead* LocateFileBlockForAddress( inline const FileBlockHead* LocateFileBlockForAddress(
const Pointer & ptrval, const Pointer & ptrval,

View File

@ -181,8 +181,7 @@ void Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabas
// sanity check, should never happen if the genblenddna script is right // sanity check, should never happen if the genblenddna script is right
if (!(f->flags & FieldFlag_Pointer)) { if (!(f->flags & FieldFlag_Pointer)) {
throw Error((Formatter::format(),"Field `",name,"` of structure `", throw Error((Formatter::format(),"Field `",name,"` of structure `",
this->name,"` ought to be a pointer" this->name,"` ought to be a pointer"));
));
} }
db.reader->IncPtr(f->offset); db.reader->IncPtr(f->offset);
@ -204,9 +203,57 @@ void Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabas
db.reader->SetCurrentPos(old); db.reader->SetCurrentPos(old);
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
if (out) { ++db.stats().fields_read;
++db.stats().pointers_resolved; #endif
}
//--------------------------------------------------------------------------------
template <int error_policy, template <typename> class TOUT, typename T, size_t N>
void Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
const FileDatabase& db) const
{
// XXX see if we can reduce this to call to the 'normal' ReadFieldPtr
const StreamReaderAny::pos old = db.reader->GetCurrentPos();
Pointer ptrval[N];
const Field* f;
try {
f = &(*this)[name];
// sanity check, should never happen if the genblenddna script is right
if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) {
throw Error((Formatter::format(),"Field `",name,"` of structure `",
this->name,"` ought to be a pointer AND an array"));
} }
db.reader->IncPtr(f->offset);
size_t i = 0;
for(; i < std::min(f->array_sizes[0],N); ++i) {
Convert(ptrval[i],db);
}
for(; i < N; ++i) {
_defaultInitializer<ErrorPolicy_Igno>()(ptrval[i]);
}
// actually it is meaningless on which Structure the Convert is called
// because the `Pointer` argument triggers a special implementation.
}
catch (const Error& e) {
_defaultInitializer<error_policy>()(out,e.what());
for(size_t i = 0; i < N; ++i) {
out[i].reset();
}
return;
}
for(size_t i = 0; i < N; ++i) {
// resolve the pointer and load the corresponding structure
ResolvePointer(out[i],ptrval[i],db,*f);
}
// and recover the previous stream position
db.reader->SetCurrentPos(old);
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
++db.stats().fields_read; ++db.stats().fields_read;
#endif #endif
} }
@ -282,6 +329,29 @@ void Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const Fil
} }
db.reader->SetCurrentPos(pold); db.reader->SetCurrentPos(pold);
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
if(out) {
++db.stats().pointers_resolved;
}
#endif
}
//--------------------------------------------------------------------------------
inline void Structure :: ResolvePointer( boost::shared_ptr< FileOffset >& out, const Pointer & ptrval, const FileDatabase& db, const Field& f) const
{
// Currently used exclusively by PackedFile::data to represent
// a simple offset into the mapped BLEND file.
out.reset();
if (!ptrval.val) {
return;
}
// find the file block the pointer is pointing to
const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
out = boost::shared_ptr< FileOffset > (new FileOffset());
out->val = block->start+ static_cast<size_t>((ptrval.val - block->address.val) );
} }
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
@ -372,6 +442,10 @@ template <> void Structure :: ResolvePointer<boost::shared_ptr,ElemBase>(boost::
// cache the object now that construction is complete // cache the object now that construction is complete
// FIXME we need to do this in ConvertBlobToStructure // FIXME we need to do this in ConvertBlobToStructure
db.cache(out).set(s,out,ptrval); db.cache(out).set(s,out,ptrval);
#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
++db.stats().pointers_resolved;
#endif
} }
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------

View File

@ -75,9 +75,12 @@ static const aiLoaderDesc blenderDesc = {
namespace Assimp { namespace Assimp {
namespace Blender { namespace Blender {
// --------------------------------------------------------------------
/** Mini smart-array to avoid pulling in even more boost stuff. usable with vector and deque */ /** Mini smart-array to avoid pulling in even more boost stuff. usable with vector and deque */
// --------------------------------------------------------------------
template <template <typename,typename> class TCLASS, typename T> template <template <typename,typename> class TCLASS, typename T>
struct TempArray { struct TempArray {
typedef TCLASS< T*,std::allocator<T*> > mywrap;
TempArray() { TempArray() {
} }
@ -92,19 +95,23 @@ namespace Blender {
arr.clear(); arr.clear();
} }
TCLASS< T*,std::allocator<T*> >* operator -> () { mywrap* operator -> () {
return &arr; return &arr;
} }
operator TCLASS< T*,std::allocator<T*> > () { operator mywrap& () {
return arr; return arr;
} }
TCLASS< T*,std::allocator<T*> >& get () { operator const mywrap& () const {
return arr; return arr;
} }
const TCLASS< T*,std::allocator<T*> >& get () const { mywrap& get () {
return arr;
}
const mywrap& get () const {
return arr; return arr;
} }
@ -121,24 +128,73 @@ namespace Blender {
} }
private: private:
TCLASS< T*,std::allocator<T*> > arr; mywrap arr;
}; };
#ifdef _MSC_VER
# pragma warning(disable:4351)
#endif
// --------------------------------------------------------------------
/** ConversionData acts as intermediate storage location for /** ConversionData acts as intermediate storage location for
* the various ConvertXXX routines in BlenderImporter.*/ * the various ConvertXXX routines in BlenderImporter.*/
struct ConversionData { // --------------------------------------------------------------------
struct ConversionData
{
ConversionData(const FileDatabase& db)
: sentinel_cnt()
, next_texture()
, db(db)
{}
std::set<const Object*> objects; std::set<const Object*> objects;
TempArray <std::vector, aiMesh> meshes; TempArray <std::vector, aiMesh> meshes;
TempArray <std::vector, aiCamera> cameras; TempArray <std::vector, aiCamera> cameras;
TempArray <std::vector, aiLight> lights; TempArray <std::vector, aiLight> lights;
TempArray <std::vector, aiMaterial> materials; TempArray <std::vector, aiMaterial> materials;
TempArray <std::vector, aiTexture> textures;
// set of all materials referenced by at least one mesh in the scene // set of all materials referenced by at least one mesh in the scene
std::deque< boost::shared_ptr< Material > > materials_raw; std::deque< boost::shared_ptr< Material > > materials_raw;
// counter to name sentinel textures inserted as substitutes for procedural textures.
unsigned int sentinel_cnt;
// next texture ID for each texture type, respectively
unsigned int next_texture[aiTextureType_UNKNOWN+1];
// original file data
const FileDatabase& db;
}; };
#ifdef _MSC_VER
# pragma warning(default:4351)
#endif
// ------------------------------------------------------------------------------------------------
const char* GetTextureTypeDisplayString(Tex::Type t)
{
switch (t) {
case Tex::Type_CLOUDS : return "Clouds";
case Tex::Type_WOOD : return "Wood";
case Tex::Type_MARBLE : return "Marble";
case Tex::Type_MAGIC : return "Magic";
case Tex::Type_BLEND : return "Blend";
case Tex::Type_STUCCI : return "Stucci";
case Tex::Type_NOISE : return "Noise";
case Tex::Type_PLUGIN : return "Plugin";
case Tex::Type_MUSGRAVE : return "Musgrave";
case Tex::Type_VORONOI : return "Voronoi";
case Tex::Type_DISTNOISE : return "DistortedNoise";
case Tex::Type_ENVMAP : return "EnvMap";
case Tex::Type_IMAGE : return "Image";
default:
break;
}
return "<Unknown>";
} }
}
} // ! Blender
} // ! Assimp
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
@ -220,7 +276,7 @@ void BlenderImporter::InternReadFile( const std::string& pFile,
Scene scene; Scene scene;
ExtractScene(scene,file); ExtractScene(scene,file);
ConvertBlendFile(pScene,scene); ConvertBlendFile(pScene,scene,file);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -294,9 +350,9 @@ void BlenderImporter::ExtractScene(Scene& out, const FileDatabase& file)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void BlenderImporter::ConvertBlendFile(aiScene* out, const Scene& in) void BlenderImporter::ConvertBlendFile(aiScene* out, const Scene& in,const FileDatabase& file)
{ {
ConversionData conv; ConversionData conv(file);
// FIXME it must be possible to take the hierarchy directly from // FIXME it must be possible to take the hierarchy directly from
// the file. This is terrible. Here, we're first looking for // the file. This is terrible. Here, we're first looking for
@ -357,6 +413,11 @@ void BlenderImporter::ConvertBlendFile(aiScene* out, const Scene& in)
conv.materials.dismiss(); conv.materials.dismiss();
} }
if (conv.textures->size()) {
out->mTextures = new aiTexture*[out->mNumTextures = static_cast<unsigned int>( conv.textures->size() )];
std::copy(conv.textures->begin(),conv.textures->end(),out->mTextures);
conv.textures.dismiss();
}
// acknowledge that the scene might come out incomplete // acknowledge that the scene might come out incomplete
// by Assimps definition of `complete`: blender scenes // by Assimps definition of `complete`: blender scenes
@ -367,6 +428,113 @@ void BlenderImporter::ConvertBlendFile(aiScene* out, const Scene& in)
} }
} }
// ------------------------------------------------------------------------------------------------
void BlenderImporter::ResolveImage(MaterialHelper* out, const Material* mat, const MTex* tex, const Image* img, ConversionData& conv_data)
{
mat; tex; conv_data;
aiString name;
// check if the file contents are bundled with the BLEND file
if (img->packedfile) {
name.data[0] = '*';
name.length = 1+ ASSIMP_itoa10(name.data+1,MAXLEN-1,conv_data.textures->size());
conv_data.textures->push_back(new aiTexture());
aiTexture* tex = conv_data.textures->back();
// usually 'img->name' will be the original file name of the embedded textures,
// so we can extract the file extension from it.
const size_t nlen = strlen( img->name );
const char* s = img->name+nlen, *e = s;
while (s >= img->name && *s != '.')--s;
tex->achFormatHint[0] = s+1>e ? '\0' : s[1];
tex->achFormatHint[1] = s+2>e ? '\0' : s[2];
tex->achFormatHint[2] = s+3>e ? '\0' : s[3];
tex->achFormatHint[3] = '\0';
// tex->mHeight = 0;
tex->mWidth = img->packedfile->size;
uint8_t* ch = new uint8_t[tex->mWidth];
conv_data.db.reader->SetCurrentPos(img->packedfile->data->val);
conv_data.db.reader->CopyAndAdvance(ch,tex->mWidth);
tex->pcData = reinterpret_cast<aiTexel*>(ch);
LogInfo("Reading embedded texture, original file was "+std::string(img->name));
}
else {
name = aiString( img->name );
}
out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(
conv_data.next_texture[aiTextureType_DIFFUSE]++)
);
}
// ------------------------------------------------------------------------------------------------
void BlenderImporter::AddSentinelTexture(MaterialHelper* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
{
mat; tex; conv_data;
aiString name;
name.length = sprintf(name.data, "Procedural,num=%i,type=%s",conv_data.sentinel_cnt++,
GetTextureTypeDisplayString(tex->tex->type)
);
out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(
conv_data.next_texture[aiTextureType_DIFFUSE]++)
);
}
// ------------------------------------------------------------------------------------------------
void BlenderImporter::ResolveTexture(MaterialHelper* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
{
const Tex* rtex = tex->tex.get();
if(!rtex || !rtex->type) {
return;
}
// We can't support most of the texture types because the're mostly procedural.
// These are substituted by a dummy texture.
const char* dispnam = "";
switch( rtex->type )
{
// these are listed in blender's UI
case Tex::Type_CLOUDS :
case Tex::Type_WOOD :
case Tex::Type_MARBLE :
case Tex::Type_MAGIC :
case Tex::Type_BLEND :
case Tex::Type_STUCCI :
case Tex::Type_NOISE :
case Tex::Type_PLUGIN :
case Tex::Type_MUSGRAVE :
case Tex::Type_VORONOI :
case Tex::Type_DISTNOISE :
case Tex::Type_ENVMAP :
// these do no appear in the UI, why?
case Tex::Type_POINTDENSITY :
case Tex::Type_VOXELDATA :
LogWarn(std::string("Encountered a texture with an unsupported type: ")+dispnam);
AddSentinelTexture(out, mat, tex, conv_data);
break;
case Tex::Type_IMAGE :
if (!rtex->ima) {
LogError("A texture claims to be an Image, but no image reference is given");
break;
}
ResolveImage(out, mat, tex, rtex->ima.get(),conv_data);
break;
default:
ai_assert(false);
};
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void BlenderImporter::BuildMaterials(ConversionData& conv_data) void BlenderImporter::BuildMaterials(ConversionData& conv_data)
{ {
@ -386,6 +554,7 @@ void BlenderImporter::BuildMaterials(ConversionData& conv_data)
p->r = p->g = p->b = 0.6f; p->r = p->g = p->b = 0.6f;
p->specr = p->specg = p->specb = 0.6f; p->specr = p->specg = p->specb = 0.6f;
p->ambir = p->ambig = p->ambib = 0.0f; p->ambir = p->ambig = p->ambib = 0.0f;
p->mirr = p->mirg = p->mirb = 0.0f;
p->emit = 0.f; p->emit = 0.f;
p->alpha = 0.f; p->alpha = 0.f;
@ -402,6 +571,11 @@ void BlenderImporter::BuildMaterials(ConversionData& conv_data)
for_each(boost::shared_ptr<Material> mat, conv_data.materials_raw) { for_each(boost::shared_ptr<Material> mat, conv_data.materials_raw) {
// reset per material global counters
for (size_t i = 0; i < sizeof(conv_data.next_texture)/sizeof(conv_data.next_texture[0]);++i) {
conv_data.next_texture[i] = 0 ;
}
MaterialHelper* mout = new MaterialHelper(); MaterialHelper* mout = new MaterialHelper();
conv_data.materials->push_back(mout); conv_data.materials->push_back(mout);
@ -419,6 +593,17 @@ void BlenderImporter::BuildMaterials(ConversionData& conv_data)
col = aiColor3D(mat->ambir,mat->ambig,mat->ambib); col = aiColor3D(mat->ambir,mat->ambig,mat->ambib);
mout->AddProperty(&col,1,AI_MATKEY_COLOR_AMBIENT); mout->AddProperty(&col,1,AI_MATKEY_COLOR_AMBIENT);
col = aiColor3D(mat->mirr,mat->mirg,mat->mirb);
mout->AddProperty(&col,1,AI_MATKEY_COLOR_REFLECTIVE);
for(size_t i = 0; i < sizeof(mat->mtex) / sizeof(mat->mtex[0]); ++i) {
if (!mat->mtex[i]) {
continue;
}
ResolveTexture(mout,mat.get(),mat->mtex[i].get(),conv_data);
}
} }
} }
@ -451,11 +636,11 @@ void BlenderImporter::ConvertMesh(const Scene& in, const Object* obj, const Mesh
} }
// some sanity checks // some sanity checks
if (mesh->totface > mesh->mface.size() ){ if (static_cast<size_t> ( mesh->totface ) > mesh->mface.size() ){
ThrowException("Number of faces is larger than the corresponding array"); ThrowException("Number of faces is larger than the corresponding array");
} }
if (mesh->totvert > mesh->mvert.size()) { if (static_cast<size_t> ( mesh->totvert ) > mesh->mvert.size()) {
ThrowException("Number of vertices is larger than the corresponding array"); ThrowException("Number of vertices is larger than the corresponding array");
} }

View File

@ -66,6 +66,9 @@ namespace Assimp {
struct Mesh; struct Mesh;
struct Camera; struct Camera;
struct Lamp; struct Lamp;
struct MTex;
struct Image;
struct Material;
} }
// BlenderLoader.cpp // BlenderLoader.cpp
@ -157,7 +160,8 @@ protected:
// -------------------- // --------------------
void ConvertBlendFile(aiScene* out, void ConvertBlendFile(aiScene* out,
const Blender::Scene& in const Blender::Scene& in,
const Blender::FileDatabase& file
); );
private: private:
@ -191,7 +195,33 @@ private:
); );
// -------------------- // --------------------
void BuildMaterials(Blender::ConversionData& conv_data) ; void BuildMaterials(
Blender::ConversionData& conv_data
) ;
// --------------------
void ResolveTexture(
MaterialHelper* out,
const Blender::Material* mat,
const Blender::MTex* tex,
Blender::ConversionData& conv_data
);
// --------------------
void ResolveImage(
MaterialHelper* out,
const Blender::Material* mat,
const Blender::MTex* tex,
const Blender::Image* img,
Blender::ConversionData& conv_data
);
void AddSentinelTexture(
MaterialHelper* out,
const Blender::Material* mat,
const Blender::MTex* tex,
Blender::ConversionData& conv_data
);
private: // static stuff, mostly logging and error reporting. private: // static stuff, mostly logging and error reporting.

View File

@ -207,6 +207,9 @@ template <> void Structure :: Convert<PackedFile> (
) const ) const
{ {
ReadField<ErrorPolicy_Warn>(dest.size,"size",db);
ReadField<ErrorPolicy_Warn>(dest.seek,"seek",db);
ReadFieldPtr<ErrorPolicy_Warn>(dest.data,"*data",db);
db.reader->IncPtr(size); db.reader->IncPtr(size);
} }
@ -271,6 +274,7 @@ template <> void Structure :: Convert<Material> (
ReadFieldPtr<ErrorPolicy_Igno>(dest.group,"*group",db); ReadFieldPtr<ErrorPolicy_Igno>(dest.group,"*group",db);
ReadField<ErrorPolicy_Warn>(dest.diff_shader,"diff_shader",db); ReadField<ErrorPolicy_Warn>(dest.diff_shader,"diff_shader",db);
ReadField<ErrorPolicy_Warn>(dest.spec_shader,"spec_shader",db); ReadField<ErrorPolicy_Warn>(dest.spec_shader,"spec_shader",db);
ReadFieldPtr<ErrorPolicy_Igno>(dest.mtex,"*mtex",db);
db.reader->IncPtr(size); db.reader->IncPtr(size);
} }
@ -338,7 +342,7 @@ template <> void Structure :: Convert<MVert> (
ReadFieldArray<ErrorPolicy_Fail>(dest.no,"no",db); ReadFieldArray<ErrorPolicy_Fail>(dest.no,"no",db);
ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db); ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db); ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db);
ReadField<ErrorPolicy_Fail>(dest.bweight,"bweight",db); ReadField<ErrorPolicy_Igno>(dest.bweight,"bweight",db);
db.reader->IncPtr(size); db.reader->IncPtr(size);
} }

View File

@ -92,6 +92,7 @@ namespace Assimp {
#define FAIL // fail the import if the field does not exist #define FAIL // fail the import if the field does not exist
struct Object; struct Object;
struct MTex;
#define AI_BLEND_MESH_MAX_VERTS 2000000000L #define AI_BLEND_MESH_MAX_VERTS 2000000000L
@ -112,9 +113,9 @@ struct ListBase : ElemBase {
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
struct PackedFile : ElemBase { struct PackedFile : ElemBase {
// int size; int size WARN;
// int seek; int seek WARN;
// void* data; boost::shared_ptr< FileOffset > data WARN;
}; };
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
@ -144,7 +145,7 @@ struct MVert : ElemBase {
float no[3] FAIL; float no[3] FAIL;
char flag; char flag;
int mat_nr WARN; int mat_nr WARN;
int bweight FAIL; int bweight;
}; };
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
@ -222,7 +223,7 @@ struct Material : ElemBase {
short diff_shader WARN; short diff_shader WARN;
short spec_shader WARN; short spec_shader WARN;
//MTex *mtex[18]; boost::shared_ptr<MTex> mtex[18];
}; };
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------