nuclear@0: nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file Implementation of the AC3D importer class */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_AC_IMPORTER nuclear@0: nuclear@0: // internal headers nuclear@0: #include "ACLoader.h" nuclear@0: #include "ParsingUtils.h" nuclear@0: #include "fast_atof.h" nuclear@0: #include "Subdivision.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: static const aiImporterDesc desc = { nuclear@0: "AC3D Importer", nuclear@0: "", nuclear@0: "", nuclear@0: "", nuclear@0: aiImporterFlags_SupportTextFlavour, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: "ac acc ac3d" nuclear@0: }; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // skip to the next token nuclear@0: #define AI_AC_SKIP_TO_NEXT_TOKEN() \ nuclear@0: if (!SkipSpaces(&buffer)) \ nuclear@0: { \ nuclear@0: DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL"); \ nuclear@0: continue; \ nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read a string (may be enclosed in double quotation marks). buffer must point to " nuclear@0: #define AI_AC_GET_STRING(out) \ nuclear@0: ++buffer; \ nuclear@0: const char* sz = buffer; \ nuclear@0: while ('\"' != *buffer) \ nuclear@0: { \ nuclear@0: if (IsLineEnd( *buffer )) \ nuclear@0: { \ nuclear@0: DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL in string"); \ nuclear@0: out = "ERROR"; \ nuclear@0: break; \ nuclear@0: } \ nuclear@0: ++buffer; \ nuclear@0: } \ nuclear@0: if (IsLineEnd( *buffer ))continue; \ nuclear@0: out = std::string(sz,(unsigned int)(buffer-sz)); \ nuclear@0: ++buffer; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // read 1 to n floats prefixed with an optional predefined identifier nuclear@0: #define AI_AC_CHECKED_LOAD_FLOAT_ARRAY(name,name_length,num,out) \ nuclear@0: AI_AC_SKIP_TO_NEXT_TOKEN(); \ nuclear@0: if (name_length) \ nuclear@0: { \ nuclear@0: if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \ nuclear@0: { \ nuclear@0: DefaultLogger::get()->error("AC3D: Unexpexted token. " name " was expected."); \ nuclear@0: continue; \ nuclear@0: } \ nuclear@0: buffer += name_length+1; \ nuclear@0: } \ nuclear@0: for (unsigned int i = 0; i < num;++i) \ nuclear@0: { \ nuclear@0: AI_AC_SKIP_TO_NEXT_TOKEN(); \ nuclear@0: buffer = fast_atoreal_move(buffer,((float*)out)[i]); \ nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: AC3DImporter::AC3DImporter() nuclear@0: { nuclear@0: // nothing to be done here nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: AC3DImporter::~AC3DImporter() nuclear@0: { nuclear@0: // nothing to be done here nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const nuclear@0: { nuclear@0: std::string extension = GetExtension(pFile); nuclear@0: nuclear@0: // fixme: are acc and ac3d *really* used? Some sources say they are nuclear@0: if(extension == "ac" || extension == "ac3d" || extension == "acc") { nuclear@0: return true; nuclear@0: } nuclear@0: if (!extension.length() || checkSig) { nuclear@0: uint32_t token = AI_MAKE_MAGIC("AC3D"); nuclear@0: return CheckMagicToken(pIOHandler,pFile,&token,1,0); nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Loader meta information nuclear@0: const aiImporterDesc* AC3DImporter::GetInfo () const nuclear@0: { nuclear@0: return &desc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Get a pointer to the next line from the file nuclear@0: bool AC3DImporter::GetNextLine( ) nuclear@0: { nuclear@0: SkipLine(&buffer); nuclear@0: return SkipSpaces(&buffer); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Parse an object section in an AC file nuclear@0: void AC3DImporter::LoadObjectSection(std::vector& objects) nuclear@0: { nuclear@0: if (!TokenMatch(buffer,"OBJECT",6)) nuclear@0: return; nuclear@0: nuclear@0: SkipSpaces(&buffer); nuclear@0: nuclear@0: ++mNumMeshes; nuclear@0: nuclear@0: objects.push_back(Object()); nuclear@0: Object& obj = objects.back(); nuclear@0: nuclear@0: aiLight* light = NULL; nuclear@0: if (!ASSIMP_strincmp(buffer,"light",5)) nuclear@0: { nuclear@0: // This is a light source. Add it to the list nuclear@0: mLights->push_back(light = new aiLight()); nuclear@0: nuclear@0: // Return a point light with no attenuation nuclear@0: light->mType = aiLightSource_POINT; nuclear@0: light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f,1.f,1.f); nuclear@0: light->mAttenuationConstant = 1.f; nuclear@0: nuclear@0: // Generate a default name for both the light source and the node nuclear@0: // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version. nuclear@0: light->mName.length = ::sprintf(light->mName.data,"ACLight_%i",static_cast(mLights->size())-1); nuclear@0: obj.name = std::string( light->mName.data ); nuclear@0: nuclear@0: DefaultLogger::get()->debug("AC3D: Light source encountered"); nuclear@0: obj.type = Object::Light; nuclear@0: } nuclear@0: else if (!ASSIMP_strincmp(buffer,"group",5)) nuclear@0: { nuclear@0: obj.type = Object::Group; nuclear@0: } nuclear@0: else if (!ASSIMP_strincmp(buffer,"world",5)) nuclear@0: { nuclear@0: obj.type = Object::World; nuclear@0: } nuclear@0: else obj.type = Object::Poly; nuclear@0: while (GetNextLine()) nuclear@0: { nuclear@0: if (TokenMatch(buffer,"kids",4)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: unsigned int num = strtoul10(buffer,&buffer); nuclear@0: GetNextLine(); nuclear@0: if (num) nuclear@0: { nuclear@0: // load the children of this object recursively nuclear@0: obj.children.reserve(num); nuclear@0: for (unsigned int i = 0; i < num; ++i) nuclear@0: LoadObjectSection(obj.children); nuclear@0: } nuclear@0: return; nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"name",4)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: AI_AC_GET_STRING(obj.name); nuclear@0: nuclear@0: // If this is a light source, we'll also need to store nuclear@0: // the name of the node in it. nuclear@0: if (light) nuclear@0: { nuclear@0: light->mName.Set(obj.name); nuclear@0: } nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"texture",7)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: AI_AC_GET_STRING(obj.texture); nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"texrep",6)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat); nuclear@0: if (!obj.texRepeat.x || !obj.texRepeat.y) nuclear@0: obj.texRepeat = aiVector2D (1.f,1.f); nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"texoff",6)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset); nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"rot",3)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,9,&obj.rotation); nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"loc",3)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation); nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"subdiv",6)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: obj.subDiv = strtoul10(buffer,&buffer); nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"crease",6)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: obj.crease = fast_atof(buffer); nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"numvert",7)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: nuclear@0: unsigned int t = strtoul10(buffer,&buffer); nuclear@0: obj.vertices.reserve(t); nuclear@0: for (unsigned int i = 0; i < t;++i) nuclear@0: { nuclear@0: if (!GetNextLine()) nuclear@0: { nuclear@0: DefaultLogger::get()->error("AC3D: Unexpected EOF: not all vertices have been parsed yet"); nuclear@0: break; nuclear@0: } nuclear@0: else if (!IsNumeric(*buffer)) nuclear@0: { nuclear@0: DefaultLogger::get()->error("AC3D: Unexpected token: not all vertices have been parsed yet"); nuclear@0: --buffer; // make sure the line is processed a second time nuclear@0: break; nuclear@0: } nuclear@0: obj.vertices.push_back(aiVector3D()); nuclear@0: aiVector3D& v = obj.vertices.back(); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&v.x); nuclear@0: } nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"numsurf",7)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: nuclear@0: bool Q3DWorkAround = false; nuclear@0: nuclear@0: const unsigned int t = strtoul10(buffer,&buffer); nuclear@0: obj.surfaces.reserve(t); nuclear@0: for (unsigned int i = 0; i < t;++i) nuclear@0: { nuclear@0: GetNextLine(); nuclear@0: if (!TokenMatch(buffer,"SURF",4)) nuclear@0: { nuclear@0: // FIX: this can occur for some files - Quick 3D for nuclear@0: // example writes no surf chunks nuclear@0: if (!Q3DWorkAround) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("AC3D: SURF token was expected"); nuclear@0: DefaultLogger::get()->debug("Continuing with Quick3D Workaround enabled"); nuclear@0: } nuclear@0: --buffer; // make sure the line is processed a second time nuclear@0: // break; --- see fix notes above nuclear@0: nuclear@0: Q3DWorkAround = true; nuclear@0: } nuclear@0: SkipSpaces(&buffer); nuclear@0: obj.surfaces.push_back(Surface()); nuclear@0: Surface& surf = obj.surfaces.back(); nuclear@0: surf.flags = strtoul_cppstyle(buffer); nuclear@0: nuclear@0: while (1) nuclear@0: { nuclear@0: if(!GetNextLine()) nuclear@0: { nuclear@0: DefaultLogger::get()->error("AC3D: Unexpected EOF: surface is incomplete"); nuclear@0: break; nuclear@0: } nuclear@0: if (TokenMatch(buffer,"mat",3)) nuclear@0: { nuclear@0: SkipSpaces(&buffer); nuclear@0: surf.mat = strtoul10(buffer); nuclear@0: } nuclear@0: else if (TokenMatch(buffer,"refs",4)) nuclear@0: { nuclear@0: // --- see fix notes above nuclear@0: if (Q3DWorkAround) nuclear@0: { nuclear@0: if (!surf.entries.empty()) nuclear@0: { nuclear@0: buffer -= 6; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: SkipSpaces(&buffer); nuclear@0: const unsigned int m = strtoul10(buffer); nuclear@0: surf.entries.reserve(m); nuclear@0: nuclear@0: obj.numRefs += m; nuclear@0: nuclear@0: for (unsigned int k = 0; k < m; ++k) nuclear@0: { nuclear@0: if(!GetNextLine()) nuclear@0: { nuclear@0: DefaultLogger::get()->error("AC3D: Unexpected EOF: surface references are incomplete"); nuclear@0: break; nuclear@0: } nuclear@0: surf.entries.push_back(Surface::SurfaceEntry()); nuclear@0: Surface::SurfaceEntry& entry = surf.entries.back(); nuclear@0: nuclear@0: entry.first = strtoul10(buffer,&buffer); nuclear@0: SkipSpaces(&buffer); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&entry.second); nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: nuclear@0: --buffer; // make sure the line is processed a second time nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: DefaultLogger::get()->error("AC3D: Unexpected EOF: \'kids\' line was expected"); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Convert a material from AC3DImporter::Material to aiMaterial nuclear@0: void AC3DImporter::ConvertMaterial(const Object& object, nuclear@0: const Material& matSrc, nuclear@0: aiMaterial& matDest) nuclear@0: { nuclear@0: aiString s; nuclear@0: nuclear@0: if (matSrc.name.length()) nuclear@0: { nuclear@0: s.Set(matSrc.name); nuclear@0: matDest.AddProperty(&s,AI_MATKEY_NAME); nuclear@0: } nuclear@0: if (object.texture.length()) nuclear@0: { nuclear@0: s.Set(object.texture); nuclear@0: matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); nuclear@0: nuclear@0: // UV transformation nuclear@0: if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y || nuclear@0: object.texOffset.x || object.texOffset.y) nuclear@0: { nuclear@0: aiUVTransform transform; nuclear@0: transform.mScaling = object.texRepeat; nuclear@0: transform.mTranslation = object.texOffset; nuclear@0: matDest.AddProperty(&transform,1,AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: matDest.AddProperty(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE); nuclear@0: matDest.AddProperty(&matSrc.amb,1, AI_MATKEY_COLOR_AMBIENT); nuclear@0: matDest.AddProperty(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE); nuclear@0: matDest.AddProperty(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR); nuclear@0: nuclear@0: int n; nuclear@0: if (matSrc.shin) nuclear@0: { nuclear@0: n = aiShadingMode_Phong; nuclear@0: matDest.AddProperty(&matSrc.shin,1,AI_MATKEY_SHININESS); nuclear@0: } nuclear@0: else n = aiShadingMode_Gouraud; nuclear@0: matDest.AddProperty(&n,1,AI_MATKEY_SHADING_MODEL); nuclear@0: nuclear@0: float f = 1.f - matSrc.trans; nuclear@0: matDest.AddProperty(&f,1,AI_MATKEY_OPACITY); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Converts the loaded data to the internal verbose representation nuclear@0: aiNode* AC3DImporter::ConvertObjectSection(Object& object, nuclear@0: std::vector& meshes, nuclear@0: std::vector& outMaterials, nuclear@0: const std::vector& materials, nuclear@0: aiNode* parent) nuclear@0: { nuclear@0: aiNode* node = new aiNode(); nuclear@0: node->mParent = parent; nuclear@0: if (object.vertices.size()) nuclear@0: { nuclear@0: if (!object.surfaces.size() || !object.numRefs) nuclear@0: { nuclear@0: /* " An object with 7 vertices (no surfaces, no materials defined). nuclear@0: This is a good way of getting point data into AC3D. nuclear@0: The Vertex->create convex-surface/object can be used on these nuclear@0: vertices to 'wrap' a 3d shape around them " nuclear@0: (http://www.opencity.info/html/ac3dfileformat.html) nuclear@0: nuclear@0: therefore: if no surfaces are defined return point data only nuclear@0: */ nuclear@0: nuclear@0: DefaultLogger::get()->info("AC3D: No surfaces defined in object definition, " nuclear@0: "a point list is returned"); nuclear@0: nuclear@0: meshes.push_back(new aiMesh()); nuclear@0: aiMesh* mesh = meshes.back(); nuclear@0: nuclear@0: mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size(); nuclear@0: aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; nuclear@0: aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; nuclear@0: nuclear@0: for (unsigned int i = 0; i < mesh->mNumVertices;++i,++faces,++verts) nuclear@0: { nuclear@0: *verts = object.vertices[i]; nuclear@0: faces->mNumIndices = 1; nuclear@0: faces->mIndices = new unsigned int[1]; nuclear@0: faces->mIndices[0] = i; nuclear@0: } nuclear@0: nuclear@0: // use the primary material in this case. this should be the nuclear@0: // default material if all objects of the file contain points nuclear@0: // and no faces. nuclear@0: mesh->mMaterialIndex = 0; nuclear@0: outMaterials.push_back(new aiMaterial()); nuclear@0: ConvertMaterial(object, materials[0], *outMaterials.back()); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // need to generate one or more meshes for this object. nuclear@0: // find out how many different materials we have nuclear@0: typedef std::pair< unsigned int, unsigned int > IntPair; nuclear@0: typedef std::vector< IntPair > MatTable; nuclear@0: MatTable needMat(materials.size(),IntPair(0,0)); nuclear@0: nuclear@0: std::vector::iterator it,end = object.surfaces.end(); nuclear@0: std::vector::iterator it2,end2; nuclear@0: nuclear@0: for (it = object.surfaces.begin(); it != end; ++it) nuclear@0: { nuclear@0: register unsigned int idx = (*it).mat; nuclear@0: if (idx >= needMat.size()) nuclear@0: { nuclear@0: DefaultLogger::get()->error("AC3D: material index is out of range"); nuclear@0: idx = 0; nuclear@0: } nuclear@0: if ((*it).entries.empty()) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("AC3D: surface her zero vertex references"); nuclear@0: } nuclear@0: nuclear@0: // validate all vertex indices to make sure we won't crash here nuclear@0: for (it2 = (*it).entries.begin(), nuclear@0: end2 = (*it).entries.end(); it2 != end2; ++it2) nuclear@0: { nuclear@0: if ((*it2).first >= object.vertices.size()) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("AC3D: Invalid vertex reference"); nuclear@0: (*it2).first = 0; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!needMat[idx].first)++node->mNumMeshes; nuclear@0: nuclear@0: switch ((*it).flags & 0xf) nuclear@0: { nuclear@0: // closed line nuclear@0: case 0x1: nuclear@0: nuclear@0: needMat[idx].first += (unsigned int)(*it).entries.size(); nuclear@0: needMat[idx].second += (unsigned int)(*it).entries.size()<<1u; nuclear@0: break; nuclear@0: nuclear@0: // unclosed line nuclear@0: case 0x2: nuclear@0: nuclear@0: needMat[idx].first += (unsigned int)(*it).entries.size()-1; nuclear@0: needMat[idx].second += ((unsigned int)(*it).entries.size()-1)<<1u; nuclear@0: break; nuclear@0: nuclear@0: // 0 == polygon, else unknown nuclear@0: default: nuclear@0: nuclear@0: if ((*it).flags & 0xf) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("AC3D: The type flag of a surface is unknown"); nuclear@0: (*it).flags &= ~(0xf); nuclear@0: } nuclear@0: nuclear@0: // the number of faces increments by one, the number nuclear@0: // of vertices by surface.numref. nuclear@0: needMat[idx].first++; nuclear@0: needMat[idx].second += (unsigned int)(*it).entries.size(); nuclear@0: }; nuclear@0: } nuclear@0: unsigned int* pip = node->mMeshes = new unsigned int[node->mNumMeshes]; nuclear@0: unsigned int mat = 0; nuclear@0: const size_t oldm = meshes.size(); nuclear@0: for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end(); nuclear@0: cit != cend; ++cit, ++mat) nuclear@0: { nuclear@0: if (!(*cit).first)continue; nuclear@0: nuclear@0: // allocate a new aiMesh object nuclear@0: *pip++ = (unsigned int)meshes.size(); nuclear@0: aiMesh* mesh = new aiMesh(); nuclear@0: meshes.push_back(mesh); nuclear@0: nuclear@0: mesh->mMaterialIndex = (unsigned int)outMaterials.size(); nuclear@0: outMaterials.push_back(new aiMaterial()); nuclear@0: ConvertMaterial(object, materials[mat], *outMaterials.back()); nuclear@0: nuclear@0: // allocate storage for vertices and normals nuclear@0: mesh->mNumFaces = (*cit).first; nuclear@0: aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; nuclear@0: nuclear@0: mesh->mNumVertices = (*cit).second; nuclear@0: aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; nuclear@0: unsigned int cur = 0; nuclear@0: nuclear@0: // allocate UV coordinates, but only if the texture name for the nuclear@0: // surface is not empty nuclear@0: aiVector3D* uv = NULL; nuclear@0: if(object.texture.length()) nuclear@0: { nuclear@0: uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; nuclear@0: mesh->mNumUVComponents[0] = 2; nuclear@0: } nuclear@0: nuclear@0: for (it = object.surfaces.begin(); it != end; ++it) nuclear@0: { nuclear@0: if (mat == (*it).mat) nuclear@0: { nuclear@0: const Surface& src = *it; nuclear@0: nuclear@0: // closed polygon nuclear@0: unsigned int type = (*it).flags & 0xf; nuclear@0: if (!type) nuclear@0: { nuclear@0: aiFace& face = *faces++; nuclear@0: if((face.mNumIndices = (unsigned int)src.entries.size())) nuclear@0: { nuclear@0: face.mIndices = new unsigned int[face.mNumIndices]; nuclear@0: for (unsigned int i = 0; i < face.mNumIndices;++i,++vertices) nuclear@0: { nuclear@0: const Surface::SurfaceEntry& entry = src.entries[i]; nuclear@0: face.mIndices[i] = cur++; nuclear@0: nuclear@0: // copy vertex positions nuclear@0: *vertices = object.vertices[entry.first] + object.translation; nuclear@0: nuclear@0: nuclear@0: // copy texture coordinates nuclear@0: if (uv) nuclear@0: { nuclear@0: uv->x = entry.second.x; nuclear@0: uv->y = entry.second.y; nuclear@0: ++uv; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: nuclear@0: it2 = (*it).entries.begin(); nuclear@0: nuclear@0: // either a closed or an unclosed line nuclear@0: register unsigned int tmp = (unsigned int)(*it).entries.size(); nuclear@0: if (0x2 == type)--tmp; nuclear@0: for (unsigned int m = 0; m < tmp;++m) nuclear@0: { nuclear@0: aiFace& face = *faces++; nuclear@0: nuclear@0: face.mNumIndices = 2; nuclear@0: face.mIndices = new unsigned int[2]; nuclear@0: face.mIndices[0] = cur++; nuclear@0: face.mIndices[1] = cur++; nuclear@0: nuclear@0: // copy vertex positions nuclear@0: *vertices++ = object.vertices[(*it2).first]; nuclear@0: nuclear@0: // copy texture coordinates nuclear@0: if (uv) nuclear@0: { nuclear@0: uv->x = (*it2).second.x; nuclear@0: uv->y = (*it2).second.y; nuclear@0: ++uv; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: if (0x1 == type && tmp-1 == m) nuclear@0: { nuclear@0: // if this is a closed line repeat its beginning now nuclear@0: it2 = (*it).entries.begin(); nuclear@0: } nuclear@0: else ++it2; nuclear@0: nuclear@0: // second point nuclear@0: *vertices++ = object.vertices[(*it2).first]; nuclear@0: nuclear@0: if (uv) nuclear@0: { nuclear@0: uv->x = (*it2).second.x; nuclear@0: uv->y = (*it2).second.y; nuclear@0: ++uv; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Now apply catmull clark subdivision if necessary. We split meshes into nuclear@0: // materials which is not done by AC3D during smoothing, so we need to nuclear@0: // collect all meshes using the same material group. nuclear@0: if (object.subDiv) { nuclear@0: if (configEvalSubdivision) { nuclear@0: boost::scoped_ptr div(Subdivider::Create(Subdivider::CATMULL_CLARKE)); nuclear@0: DefaultLogger::get()->info("AC3D: Evaluating subdivision surface: "+object.name); nuclear@0: nuclear@0: std::vector cpy(meshes.size()-oldm,NULL); nuclear@0: div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true); nuclear@0: std::copy(cpy.begin(),cpy.end(),meshes.begin()+oldm); nuclear@0: nuclear@0: // previous meshes are deleted vy Subdivide(). nuclear@0: } nuclear@0: else { nuclear@0: DefaultLogger::get()->info("AC3D: Letting the subdivision surface untouched due to my configuration: " nuclear@0: +object.name); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (object.name.length()) nuclear@0: node->mName.Set(object.name); nuclear@0: else nuclear@0: { nuclear@0: // generate a name depending on the type of the node nuclear@0: switch (object.type) nuclear@0: { nuclear@0: case Object::Group: nuclear@0: node->mName.length = ::sprintf(node->mName.data,"ACGroup_%i",groups++); nuclear@0: break; nuclear@0: case Object::Poly: nuclear@0: node->mName.length = ::sprintf(node->mName.data,"ACPoly_%i",polys++); nuclear@0: break; nuclear@0: case Object::Light: nuclear@0: node->mName.length = ::sprintf(node->mName.data,"ACLight_%i",lights++); nuclear@0: break; nuclear@0: nuclear@0: // there shouldn't be more than one world, but we don't care nuclear@0: case Object::World: nuclear@0: node->mName.length = ::sprintf(node->mName.data,"ACWorld_%i",worlds++); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // setup the local transformation matrix of the object nuclear@0: // compute the transformation offset to the parent node nuclear@0: node->mTransformation = aiMatrix4x4 ( object.rotation ); nuclear@0: nuclear@0: if (object.type == Object::Group || !object.numRefs) nuclear@0: { nuclear@0: node->mTransformation.a4 = object.translation.x; nuclear@0: node->mTransformation.b4 = object.translation.y; nuclear@0: node->mTransformation.c4 = object.translation.z; nuclear@0: } nuclear@0: nuclear@0: // add children to the object nuclear@0: if (object.children.size()) nuclear@0: { nuclear@0: node->mNumChildren = (unsigned int)object.children.size(); nuclear@0: node->mChildren = new aiNode*[node->mNumChildren]; nuclear@0: for (unsigned int i = 0; i < node->mNumChildren;++i) nuclear@0: { nuclear@0: node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return node; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void AC3DImporter::SetupProperties(const Importer* pImp) nuclear@0: { nuclear@0: configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL,1) ? true : false; nuclear@0: configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION,1) ? true : false; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void AC3DImporter::InternReadFile( const std::string& pFile, nuclear@0: aiScene* pScene, IOSystem* pIOHandler) nuclear@0: { nuclear@0: boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); nuclear@0: nuclear@0: // Check whether we can read from the file nuclear@0: if( file.get() == NULL) nuclear@0: throw DeadlyImportError( "Failed to open AC3D file " + pFile + "."); nuclear@0: nuclear@0: // allocate storage and copy the contents of the file to a memory buffer nuclear@0: std::vector mBuffer2; nuclear@0: TextFileToBuffer(file.get(),mBuffer2); nuclear@0: nuclear@0: buffer = &mBuffer2[0]; nuclear@0: mNumMeshes = 0; nuclear@0: nuclear@0: lights = polys = worlds = groups = 0; nuclear@0: nuclear@0: if (::strncmp(buffer,"AC3D",4)) { nuclear@0: throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found"); nuclear@0: } nuclear@0: nuclear@0: // print the file format version to the console nuclear@0: unsigned int version = HexDigitToDecimal( buffer[4] ); nuclear@0: char msg[3]; nuclear@0: ASSIMP_itoa10(msg,3,version); nuclear@0: DefaultLogger::get()->info(std::string("AC3D file format version: ") + msg); nuclear@0: nuclear@0: std::vector materials; nuclear@0: materials.reserve(5); nuclear@0: nuclear@0: std::vector rootObjects; nuclear@0: rootObjects.reserve(5); nuclear@0: nuclear@0: std::vector lights; nuclear@0: mLights = & lights; nuclear@0: nuclear@0: while (GetNextLine()) nuclear@0: { nuclear@0: if (TokenMatch(buffer,"MATERIAL",8)) nuclear@0: { nuclear@0: materials.push_back(Material()); nuclear@0: Material& mat = materials.back(); nuclear@0: nuclear@0: // manually parse the material ... sscanf would use the buldin atof ... nuclear@0: // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f nuclear@0: nuclear@0: AI_AC_SKIP_TO_NEXT_TOKEN(); nuclear@0: if ('\"' == *buffer) nuclear@0: { nuclear@0: AI_AC_GET_STRING(mat.name); nuclear@0: AI_AC_SKIP_TO_NEXT_TOKEN(); nuclear@0: } nuclear@0: nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("rgb",3,3,&mat.rgb); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("amb",3,3,&mat.amb); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("emis",4,3,&mat.emis); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("spec",4,3,&mat.spec); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("shi",3,1,&mat.shin); nuclear@0: AI_AC_CHECKED_LOAD_FLOAT_ARRAY("trans",5,1,&mat.trans); nuclear@0: } nuclear@0: LoadObjectSection(rootObjects); nuclear@0: } nuclear@0: nuclear@0: if (rootObjects.empty() || !mNumMeshes) nuclear@0: { nuclear@0: throw DeadlyImportError("AC3D: No meshes have been loaded"); nuclear@0: } nuclear@0: if (materials.empty()) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("AC3D: No material has been found"); nuclear@0: materials.push_back(Material()); nuclear@0: } nuclear@0: nuclear@0: mNumMeshes += (mNumMeshes>>2u) + 1; nuclear@0: std::vector meshes; nuclear@0: meshes.reserve(mNumMeshes); nuclear@0: nuclear@0: std::vector omaterials; nuclear@0: materials.reserve(mNumMeshes); nuclear@0: nuclear@0: // generate a dummy root if there are multiple objects on the top layer nuclear@0: Object* root; nuclear@0: if (1 == rootObjects.size()) nuclear@0: root = &rootObjects[0]; nuclear@0: else nuclear@0: { nuclear@0: root = new Object(); nuclear@0: } nuclear@0: nuclear@0: // now convert the imported stuff to our output data structure nuclear@0: pScene->mRootNode = ConvertObjectSection(*root,meshes,omaterials,materials); nuclear@0: if (1 != rootObjects.size())delete root; nuclear@0: nuclear@0: if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4)) nuclear@0: pScene->mRootNode->mName.Set(""); nuclear@0: nuclear@0: // copy meshes nuclear@0: if (meshes.empty()) nuclear@0: { nuclear@0: throw DeadlyImportError("An unknown error occured during converting"); nuclear@0: } nuclear@0: pScene->mNumMeshes = (unsigned int)meshes.size(); nuclear@0: pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; nuclear@0: ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*)); nuclear@0: nuclear@0: // copy materials nuclear@0: pScene->mNumMaterials = (unsigned int)omaterials.size(); nuclear@0: pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; nuclear@0: ::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*)); nuclear@0: nuclear@0: // copy lights nuclear@0: pScene->mNumLights = (unsigned int)lights.size(); nuclear@0: if (lights.size()) nuclear@0: { nuclear@0: pScene->mLights = new aiLight*[lights.size()]; nuclear@0: ::memcpy(pScene->mLights,&lights[0],lights.size()*sizeof(void*)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: #endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER