123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602 |
- /*
- Open Asset Import Library (assimp)
- ----------------------------------------------------------------------
- Copyright (c) 2006-2025, assimp 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 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 C4DImporter.cpp
- * @brief Implementation of the Cinema4D importer class.
- */
- #ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
- // no #ifdefing here, Cinema4D support is carried out in a branch of assimp
- // where it is turned on in the CMake settings.
- #include "C4DImporter.h"
- #include <memory>
- #include <assimp/IOSystem.hpp>
- #include <assimp/scene.h>
- #include <assimp/ai_assert.h>
- #if defined(_M_X64) || defined(__amd64__)
- # define __C4D_64BIT
- #endif
- #define __PC
- #include "c4d_file.h"
- #include "default_alien_overloads.h"
- namespace {
- aiString aiStringFrom(cineware::String const & cinestring) {
- aiString result;
- cinestring.GetCString(result.data, MAXLEN-1);
- result.length = static_cast<ai_uint32>(cinestring.GetLength());
- return result;
- }
- }
- using namespace Assimp;
- using namespace cineware;
- // overload this function and fill in your own unique data
- void GetWriterInfo(int &id, String &appname) {
- id = 2424226;
- appname = "Open Asset Import Library";
- }
- namespace Assimp {
- template<> const char* LogFunctions<C4DImporter>::Prefix() {
- return "C4D: ";
- }
- }
- static constexpr aiImporterDesc desc = {
- "Cinema4D Importer",
- "",
- "",
- "",
- aiImporterFlags_SupportBinaryFlavour,
- 0,
- 0,
- 0,
- 0,
- "c4d"
- };
- // ------------------------------------------------------------------------------------------------
- bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
- const std::string& extension = GetExtension(pFile);
- if (extension == "c4d") {
- return true;
- } else if ((!extension.length() || checkSig) && pIOHandler) {
- // TODO
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- const aiImporterDesc* C4DImporter::GetInfo () const {
- return &desc;
- }
- // ------------------------------------------------------------------------------------------------
- // Imports the given file into the given scene structure.
- void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
- std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
- if( file.get() == nullptr ) {
- ThrowException("failed to open file " + pFile);
- }
- const size_t file_size = file->FileSize();
- std::vector<uint8_t> mBuffer(file_size);
- file->Read(&mBuffer[0], 1, file_size);
- Filename f;
- f.SetMemoryReadMode(&mBuffer[0], file_size);
- // open document first
- BaseDocument* doc = LoadDocument(f, SCENEFILTER_OBJECTS | SCENEFILTER_MATERIALS);
- if(doc == nullptr ) {
- ThrowException("failed to read document " + pFile);
- }
- // Generate the root-node
- pScene->mRootNode = new aiNode("<C4DRoot>");
- // convert left-handed to right-handed
- pScene->mRootNode->mTransformation.a1 = 0.01f;
- pScene->mRootNode->mTransformation.b2 = 0.01f;
- pScene->mRootNode->mTransformation.c3 = -0.01f;
- // first convert all materials
- ReadMaterials(doc->GetFirstMaterial());
- // process C4D scene-graph recursively
- try {
- RecurseHierarchy(doc->GetFirstObject(), pScene->mRootNode);
- } catch(...) {
- for(aiMesh* mesh : meshes) {
- delete mesh;
- }
- BaseDocument::Free(doc);
- throw;
- }
- BaseDocument::Free(doc);
- // copy meshes over
- pScene->mNumMeshes = static_cast<unsigned int>(meshes.size());
- pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
- std::copy(meshes.begin(), meshes.end(), pScene->mMeshes);
- // copy materials over, adding a default material if necessary
- unsigned int mat_count = static_cast<unsigned int>(materials.size());
- for(aiMesh* mesh : meshes) {
- ai_assert(mesh->mMaterialIndex <= mat_count);
- if(mesh->mMaterialIndex >= mat_count) {
- ++mat_count;
- std::unique_ptr<aiMaterial> def_material(new aiMaterial());
- const aiString name(AI_DEFAULT_MATERIAL_NAME);
- def_material->AddProperty(&name, AI_MATKEY_NAME);
- materials.push_back(def_material.release());
- break;
- }
- }
- pScene->mNumMaterials = static_cast<unsigned int>(materials.size());
- pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]();
- std::copy(materials.begin(), materials.end(), pScene->mMaterials);
- }
- // ------------------------------------------------------------------------------------------------
- bool C4DImporter::ReadShader(aiMaterial* out, BaseShader* shader) {
- // based on Cineware sample code (C4DImportExport.cpp)
- while(shader) {
- if(shader->GetType() == Xlayer) {
- BaseContainer* container = shader->GetDataInstance();
- GeData blend = container->GetData(SLA_LAYER_BLEND);
- iBlendDataType* blend_list = reinterpret_cast<iBlendDataType*>(blend.GetCustomDataType(CUSTOMDATA_BLEND_LIST));
- if (!blend_list)
- {
- LogWarn("ignoring XLayer shader: no blend list given");
- continue;
- }
- LayerShaderLayer *lsl = dynamic_cast<LayerShaderLayer*>(blend_list->m_BlendLayers.GetObject(0));
- // Ignore the actual layer blending - models for real-time rendering should not
- // use them in a non-trivial way. Just try to find textures that we can apply
- // to the model.
- while (lsl) {
- if (lsl->GetType() == TypeFolder) {
- BlendFolder* const folder = dynamic_cast<BlendFolder*>(lsl);
- LayerShaderLayer *subLsl = dynamic_cast<LayerShaderLayer*>(folder->m_Children.GetObject(0));
- while (subLsl) {
- if (subLsl->GetType() == TypeShader) {
- BlendShader* const shader = dynamic_cast<BlendShader*>(subLsl);
- if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
- return true;
- }
- }
- subLsl = subLsl->GetNext();
- }
- } else if (lsl->GetType() == TypeShader) {
- BlendShader* const shader = dynamic_cast<BlendShader*>(lsl);
- if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
- return true;
- }
- }
- lsl = lsl->GetNext();
- }
- } else if ( shader->GetType() == Xbitmap ) {
- auto const path = aiStringFrom(shader->GetFileName().GetString());
- out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0));
- return true;
- } else {
- LogWarn("ignoring shader type: ", GetObjectTypeName(shader->GetType()));
- }
- shader = shader->GetNext();
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- void C4DImporter::ReadMaterials(BaseMaterial* mat) {
- // based on Cineware sample code
- while (mat) {
- if (mat->GetType() == Mmaterial) {
- aiMaterial* out = new aiMaterial();
- material_mapping[mat] = static_cast<unsigned int>(materials.size());
- materials.push_back(out);
- auto const ai_name = aiStringFrom(mat->GetName());
- out->AddProperty(&ai_name, AI_MATKEY_NAME);
- Material& m = dynamic_cast<Material&>(*mat);
- if (m.GetChannelState(CHANNEL_COLOR)) {
- GeData data;
- mat->GetParameter(MATERIAL_COLOR_COLOR, data);
- Vector color = data.GetVector();
- mat->GetParameter(MATERIAL_COLOR_BRIGHTNESS, data);
- const Float brightness = data.GetFloat();
- color *= brightness;
- aiVector3D v;
- v.x = color.x;
- v.y = color.y;
- v.z = color.z;
- out->AddProperty(&v, 1, AI_MATKEY_COLOR_DIFFUSE);
- }
- BaseShader* const shader = m.GetShader(MATERIAL_COLOR_SHADER);
- if(shader) {
- ReadShader(out, shader);
- }
- } else {
- LogWarn("ignoring plugin material: ", GetObjectTypeName(mat->GetType()));
- }
- mat = mat->GetNext();
- }
- }
- // ------------------------------------------------------------------------------------------------
- void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) {
- ai_assert(parent != nullptr );
- std::vector<aiNode*> nodes;
- // based on Cineware sample code
- while (object) {
- const Int32 type = object->GetType();
- const Matrix& ml = object->GetMl();
- aiNode* const nd = new aiNode();
- nd->mParent = parent;
- nd->mName = aiStringFrom(object->GetName());
- nd->mTransformation.a1 = ml.v1.x;
- nd->mTransformation.b1 = ml.v1.y;
- nd->mTransformation.c1 = ml.v1.z;
- nd->mTransformation.a2 = ml.v2.x;
- nd->mTransformation.b2 = ml.v2.y;
- nd->mTransformation.c2 = ml.v2.z;
- nd->mTransformation.a3 = ml.v3.x;
- nd->mTransformation.b3 = ml.v3.y;
- nd->mTransformation.c3 = ml.v3.z;
- nd->mTransformation.a4 = ml.off.x;
- nd->mTransformation.b4 = ml.off.y;
- nd->mTransformation.c4 = ml.off.z;
- nodes.push_back(nd);
- GeData data;
- if (type == Ocamera) {
- object->GetParameter(CAMERAOBJECT_FOV, data);
- // TODO: read camera
- } else if (type == Olight) {
- // TODO: read light
- } else if (type == Opolygon) {
- aiMesh* const mesh = ReadMesh(object);
- if(mesh != nullptr) {
- nd->mNumMeshes = 1;
- nd->mMeshes = new unsigned int[1];
- nd->mMeshes[0] = static_cast<unsigned int>(meshes.size());
- meshes.push_back(mesh);
- }
- } else {
- LogWarn("ignoring object: ", GetObjectTypeName(type));
- }
- RecurseHierarchy(object->GetDown(), nd);
- object = object->GetNext();
- }
- // copy nodes over to parent
- parent->mNumChildren = static_cast<unsigned int>(nodes.size());
- parent->mChildren = new aiNode*[parent->mNumChildren]();
- std::copy(nodes.begin(), nodes.end(), parent->mChildren);
- }
- // ------------------------------------------------------------------------------------------------
- aiMesh* C4DImporter::ReadMesh(BaseObject* object) {
- ai_assert(object != nullptr);
- ai_assert( object->GetType() == Opolygon );
- // based on Cineware sample code
- PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
- ai_assert(polyObject != nullptr);
- const Int32 pointCount = polyObject->GetPointCount();
- const Int32 polyCount = polyObject->GetPolygonCount();
- if(!polyObject || !pointCount) {
- LogWarn("ignoring mesh with zero vertices or faces");
- return nullptr;
- }
- const Vector* points = polyObject->GetPointR();
- ai_assert(points != nullptr);
- const CPolygon* polys = polyObject->GetPolygonR();
- ai_assert(polys != nullptr);
- std::unique_ptr<aiMesh> mesh(new aiMesh());
- mesh->mNumFaces = static_cast<unsigned int>(polyCount);
- aiFace* face = mesh->mFaces = new aiFace[mesh->mNumFaces]();
- mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
- mesh->mMaterialIndex = 0;
- unsigned int vcount = 0;
- // first count vertices
- for (Int32 i = 0; i < polyCount; i++)
- {
- vcount += 3;
- // TODO: do we also need to handle lines or points with similar checks?
- if (polys[i].c != polys[i].d)
- {
- mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
- ++vcount;
- }
- }
- ai_assert(vcount > 0);
- mesh->mNumVertices = vcount;
- aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
- aiVector3D* normals, *uvs, *tangents, *bitangents;
- unsigned int n = 0;
- // check if there are normals, tangents or UVW coordinates
- BaseTag* tag = object->GetTag(Tnormal);
- NormalTag* normals_src = nullptr;
- if(tag) {
- normals_src = dynamic_cast<NormalTag*>(tag);
- normals = mesh->mNormals = new aiVector3D[mesh->mNumVertices]();
- }
- tag = object->GetTag(Ttangent);
- TangentTag* tangents_src = nullptr;
- if(tag) {
- tangents_src = dynamic_cast<TangentTag*>(tag);
- tangents = mesh->mTangents = new aiVector3D[mesh->mNumVertices]();
- bitangents = mesh->mBitangents = new aiVector3D[mesh->mNumVertices]();
- }
- tag = object->GetTag(Tuvw);
- UVWTag* uvs_src = nullptr;
- if(tag) {
- uvs_src = dynamic_cast<UVWTag*>(tag);
- uvs = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]();
- }
- // copy vertices and extra channels over and populate faces
- for (Int32 i = 0; i < polyCount; ++i, ++face) {
- ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
- const Vector& pointA = points[polys[i].a];
- verts->x = pointA.x;
- verts->y = pointA.y;
- verts->z = pointA.z;
- ++verts;
- ai_assert(polys[i].b < pointCount && polys[i].b >= 0);
- const Vector& pointB = points[polys[i].b];
- verts->x = pointB.x;
- verts->y = pointB.y;
- verts->z = pointB.z;
- ++verts;
- ai_assert(polys[i].c < pointCount && polys[i].c >= 0);
- const Vector& pointC = points[polys[i].c];
- verts->x = pointC.x;
- verts->y = pointC.y;
- verts->z = pointC.z;
- ++verts;
- // TODO: do we also need to handle lines or points with similar checks?
- if (polys[i].c != polys[i].d) {
- ai_assert(polys[i].d < pointCount && polys[i].d >= 0);
- face->mNumIndices = 4;
- mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
- const Vector& pointD = points[polys[i].d];
- verts->x = pointD.x;
- verts->y = pointD.y;
- verts->z = pointD.z;
- ++verts;
- } else {
- face->mNumIndices = 3;
- }
- face->mIndices = new unsigned int[face->mNumIndices];
- for(unsigned int j = 0; j < face->mNumIndices; ++j) {
- face->mIndices[j] = n++;
- }
- // copy normals
- if (normals_src) {
- if(i >= normals_src->GetDataCount()) {
- LogError("unexpected number of normals, ignoring");
- } else {
- ConstNormalHandle normal_handle = normals_src->GetDataAddressR();
- NormalStruct nor;
- NormalTag::Get(normal_handle, i, nor);
- normals->x = nor.a.x;
- normals->y = nor.a.y;
- normals->z = nor.a.z;
- ++normals;
- normals->x = nor.b.x;
- normals->y = nor.b.y;
- normals->z = nor.b.z;
- ++normals;
- normals->x = nor.c.x;
- normals->y = nor.c.y;
- normals->z = nor.c.z;
- ++normals;
- if(face->mNumIndices == 4) {
- normals->x = nor.d.x;
- normals->y = nor.d.y;
- normals->z = nor.d.z;
- ++normals;
- }
- }
- }
- // copy tangents and bitangents
- if (tangents_src) {
- for(unsigned int k = 0; k < face->mNumIndices; ++k) {
- Int32 l;
- switch(k) {
- case 0:
- l = polys[i].a;
- break;
- case 1:
- l = polys[i].b;
- break;
- case 2:
- l = polys[i].c;
- break;
- case 3:
- l = polys[i].d;
- break;
- default:
- ai_assert(false);
- }
- if(l >= tangents_src->GetDataCount()) {
- LogError("unexpected number of tangents, ignoring");
- break;
- }
- Tangent tan = tangents_src->GetDataR()[l];
- tangents->x = tan.vl.x;
- tangents->y = tan.vl.y;
- tangents->z = tan.vl.z;
- ++tangents;
- bitangents->x = tan.vr.x;
- bitangents->y = tan.vr.y;
- bitangents->z = tan.vr.z;
- ++bitangents;
- }
- }
- // copy UVs
- if (uvs_src) {
- if(i >= uvs_src->GetDataCount()) {
- LogError("unexpected number of UV coordinates, ignoring");
- }
- else {
- UVWStruct uvw;
- uvs_src->Get(uvs_src->GetDataAddressR(),i,uvw);
- uvs->x = uvw.a.x;
- uvs->y = 1.0f-uvw.a.y;
- uvs->z = uvw.a.z;
- ++uvs;
- uvs->x = uvw.b.x;
- uvs->y = 1.0f-uvw.b.y;
- uvs->z = uvw.b.z;
- ++uvs;
- uvs->x = uvw.c.x;
- uvs->y = 1.0f-uvw.c.y;
- uvs->z = uvw.c.z;
- ++uvs;
- if(face->mNumIndices == 4) {
- uvs->x = uvw.d.x;
- uvs->y = 1.0f-uvw.d.y;
- uvs->z = uvw.d.z;
- ++uvs;
- }
- }
- }
- }
- mesh->mMaterialIndex = ResolveMaterial(polyObject);
- return mesh.release();
- }
- // ------------------------------------------------------------------------------------------------
- unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj) {
- ai_assert(obj != nullptr);
- const unsigned int mat_count = static_cast<unsigned int>(materials.size());
- BaseTag* tag = obj->GetTag(Ttexture);
- if(tag == nullptr) {
- return mat_count;
- }
- TextureTag& ttag = dynamic_cast<TextureTag&>(*tag);
- BaseMaterial* const mat = ttag.GetMaterial();
- ai_assert(mat != nullptr);
- const MaterialMap::const_iterator it = material_mapping.find(mat);
- if(it == material_mapping.end()) {
- return mat_count;
- }
- ai_assert((*it).second < mat_count);
- return (*it).second;
- }
- #endif // ASSIMP_BUILD_NO_C4D_IMPORTER
|