1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2012, assimp team
6 All rights reserved.
7 
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
11 
12 * Redistributions of source code must retain the above
13   copyright notice, this list of conditions and the
14   following disclaimer.
15 
16 * Redistributions in binary form must reproduce the above
17   copyright notice, this list of conditions and the
18   following disclaimer in the documentation and/or other
19   materials provided with the distribution.
20 
21 * Neither the name of the assimp team, nor the names of its
22   contributors may be used to endorse or promote products
23   derived from this software without specific prior
24   written permission of the assimp team.
25 
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 
38 ----------------------------------------------------------------------
39 */
40 
41 /** @file  C4DImporter.cpp
42  *  @brief Implementation of the Cinema4D importer class.
43  */
44 #ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
45 
46 // no #ifdefing here, Cinema4D support is carried out in a branch of assimp
47 // where it is turned on in the CMake settings.
48 
49 #ifndef _MSC_VER
50 #   error C4D support is currently MSVC only
51 #endif
52 
53 #include "C4DImporter.h"
54 #include "TinyFormatter.h"
55 #include <memory>
56 #include <assimp/IOSystem.hpp>
57 #include <assimp/scene.h>
58 #include <assimp/ai_assert.h>
59 
60 #if defined(_M_X64) || defined(__amd64__)
61 #   define __C4D_64BIT
62 #endif
63 
64 #define __PC
65 #include "c4d_file.h"
66 #include "default_alien_overloads.h"
67 
68 using namespace melange;
69 
70 // overload this function and fill in your own unique data
GetWriterInfo(int & id,String & appname)71 void GetWriterInfo(int &id, String &appname)
72 {
73     id = 2424226;
74     appname = "Open Asset Import Library";
75 }
76 
77 using namespace Assimp;
78 using namespace Assimp::Formatter;
79 
80 namespace Assimp {
81     template<> const std::string LogFunctions<C4DImporter>::log_prefix = "C4D: ";
82 }
83 
84 static const aiImporterDesc desc = {
85     "Cinema4D Importer",
86     "",
87     "",
88     "",
89     aiImporterFlags_SupportBinaryFlavour,
90     0,
91     0,
92     0,
93     0,
94     "c4d"
95 };
96 
97 
98 // ------------------------------------------------------------------------------------------------
C4DImporter()99 C4DImporter::C4DImporter()
100 {}
101 
102 // ------------------------------------------------------------------------------------------------
~C4DImporter()103 C4DImporter::~C4DImporter()
104 {}
105 
106 // ------------------------------------------------------------------------------------------------
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const107 bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
108 {
109     const std::string& extension = GetExtension(pFile);
110     if (extension == "c4d") {
111         return true;
112     }
113 
114     else if ((!extension.length() || checkSig) && pIOHandler)   {
115         // TODO
116     }
117     return false;
118 }
119 
120 // ------------------------------------------------------------------------------------------------
GetInfo() const121 const aiImporterDesc* C4DImporter::GetInfo () const
122 {
123     return &desc;
124 }
125 
126 // ------------------------------------------------------------------------------------------------
SetupProperties(const Importer *)127 void C4DImporter::SetupProperties(const Importer* /*pImp*/)
128 {
129     // nothing to be done for the moment
130 }
131 
132 
133 // ------------------------------------------------------------------------------------------------
134 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)135 void C4DImporter::InternReadFile( const std::string& pFile,
136     aiScene* pScene, IOSystem* pIOHandler)
137 {
138     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
139 
140     if( file.get() == NULL) {
141         ThrowException("failed to open file " + pFile);
142     }
143 
144     const size_t file_size = file->FileSize();
145 
146     std::vector<uint8_t> mBuffer(file_size);
147     file->Read(&mBuffer[0], 1, file_size);
148 
149     Filename f;
150     f.SetMemoryReadMode(&mBuffer[0], file_size);
151 
152     // open document first
153     BaseDocument* doc = LoadDocument(f, SCENEFILTER_OBJECTS | SCENEFILTER_MATERIALS);
154     if(doc == NULL) {
155         ThrowException("failed to read document " + pFile);
156     }
157 
158     pScene->mRootNode = new aiNode("<C4DRoot>");
159 
160     // first convert all materials
161     ReadMaterials(doc->GetFirstMaterial());
162 
163     // process C4D scenegraph recursively
164     try {
165         RecurseHierarchy(doc->GetFirstObject(), pScene->mRootNode);
166     }
167     catch(...) {
168         for(aiMesh* mesh : meshes) {
169             delete mesh;
170         }
171         BaseDocument::Free(doc);
172         throw;
173     }
174     BaseDocument::Free(doc);
175 
176     // copy meshes over
177     pScene->mNumMeshes = static_cast<unsigned int>(meshes.size());
178     pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
179     std::copy(meshes.begin(), meshes.end(), pScene->mMeshes);
180 
181     // copy materials over, adding a default material if necessary
182     unsigned int mat_count = static_cast<unsigned int>(materials.size());
183     for(aiMesh* mesh : meshes) {
184         ai_assert(mesh->mMaterialIndex <= mat_count);
185         if(mesh->mMaterialIndex >= mat_count) {
186             ++mat_count;
187 
188             ScopeGuard<aiMaterial> def_material(new aiMaterial());
189             const aiString name(AI_DEFAULT_MATERIAL_NAME);
190             def_material->AddProperty(&name, AI_MATKEY_NAME);
191 
192             materials.push_back(def_material.dismiss());
193             break;
194         }
195     }
196 
197     pScene->mNumMaterials = static_cast<unsigned int>(materials.size());
198     pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]();
199     std::copy(materials.begin(), materials.end(), pScene->mMaterials);
200 }
201 
202 
203 // ------------------------------------------------------------------------------------------------
ReadShader(aiMaterial * out,melange::BaseShader * shader)204 bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
205 {
206     // based on Melange sample code (C4DImportExport.cpp)
207     while(shader) {
208         if(shader->GetType() == Xlayer) {
209             BaseContainer* container = shader->GetDataInstance();
210             GeData blend = container->GetData(SLA_LAYER_BLEND);
211             iBlendDataType* blend_list = reinterpret_cast<iBlendDataType*>(blend.GetCustomDataType(CUSTOMDATA_BLEND_LIST));
212             if (!blend_list)
213             {
214                 LogWarn("ignoring XLayer shader: no blend list given");
215                 continue;
216             }
217 
218             LayerShaderLayer *lsl = dynamic_cast<LayerShaderLayer*>(blend_list->m_BlendLayers.GetObject(0));
219 
220             // Ignore the actual layer blending - models for real-time rendering should not
221             // use them in a non-trivial way. Just try to find textures that we can apply
222             // to the model.
223             while (lsl)
224             {
225                 if (lsl->GetType() == TypeFolder)
226                 {
227                     BlendFolder* const folder = dynamic_cast<BlendFolder*>(lsl);
228                     LayerShaderLayer *subLsl = dynamic_cast<LayerShaderLayer*>(folder->m_Children.GetObject(0));
229 
230                     while (subLsl)
231                     {
232                         if (subLsl->GetType() == TypeShader) {
233                             BlendShader* const shader = dynamic_cast<BlendShader*>(subLsl);
234                             if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
235                                 return true;
236                             }
237                         }
238 
239                         subLsl = subLsl->GetNext();
240                     }
241                 }
242                 else if (lsl->GetType() == TypeShader) {
243                     BlendShader* const shader = dynamic_cast<BlendShader*>(lsl);
244                     if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
245                         return true;
246                     }
247                 }
248 
249                 lsl = lsl->GetNext();
250             }
251         }
252         else if ( shader->GetType() == Xbitmap )
253         {
254             aiString path;
255             shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1);
256             path.length = ::strlen(path.data);
257             out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0));
258             return true;
259         }
260         else {
261             LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType())));
262         }
263         shader = shader->GetNext();
264     }
265     return false;
266 }
267 
268 
269 // ------------------------------------------------------------------------------------------------
ReadMaterials(melange::BaseMaterial * mat)270 void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
271 {
272     // based on Melange sample code
273     while (mat)
274     {
275         const String& name = mat->GetName();
276         if (mat->GetType() == Mmaterial)
277         {
278             aiMaterial* out = new aiMaterial();
279             material_mapping[mat] = static_cast<unsigned int>(materials.size());
280             materials.push_back(out);
281 
282             aiString ai_name;
283             name.GetCString(ai_name.data, MAXLEN-1);
284             ai_name.length = ::strlen(ai_name.data);
285             out->AddProperty(&ai_name, AI_MATKEY_NAME);
286 
287             Material& m = dynamic_cast<Material&>(*mat);
288 
289             if (m.GetChannelState(CHANNEL_COLOR))
290             {
291                 GeData data;
292                 mat->GetParameter(MATERIAL_COLOR_COLOR, data);
293                 Vector color = data.GetVector();
294                 mat->GetParameter(MATERIAL_COLOR_BRIGHTNESS, data);
295                 const Float brightness = data.GetFloat();
296 
297                 color *= brightness;
298 
299                 aiVector3D v;
300                 v.x = color.x;
301                 v.y = color.y;
302                 v.z = color.z;
303                 out->AddProperty(&v, 1, AI_MATKEY_COLOR_DIFFUSE);
304             }
305 
306             BaseShader* const shader = m.GetShader(MATERIAL_COLOR_SHADER);
307             if(shader) {
308                 ReadShader(out, shader);
309             }
310         }
311         else
312         {
313             LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType())));
314         }
315         mat = mat->GetNext();
316     }
317 }
318 
319 // ------------------------------------------------------------------------------------------------
RecurseHierarchy(BaseObject * object,aiNode * parent)320 void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
321 {
322     ai_assert(parent != NULL);
323     std::vector<aiNode*> nodes;
324 
325     // based on Melange sample code
326     while (object)
327     {
328         const String& name = object->GetName();
329         const LONG type = object->GetType();
330         const Matrix& ml = object->GetMl();
331 
332         aiString string;
333         name.GetCString(string.data, MAXLEN-1);
334         string.length = ::strlen(string.data);
335         aiNode* const nd = new aiNode();
336 
337         nd->mParent = parent;
338         nd->mName = string;
339 
340         nd->mTransformation.a1 = ml.v1.x;
341         nd->mTransformation.b1 = ml.v1.y;
342         nd->mTransformation.c1 = ml.v1.z;
343 
344         nd->mTransformation.a2 = ml.v2.x;
345         nd->mTransformation.b2 = ml.v2.y;
346         nd->mTransformation.c2 = ml.v2.z;
347 
348         nd->mTransformation.a3 = ml.v3.x;
349         nd->mTransformation.b3 = ml.v3.y;
350         nd->mTransformation.c3 = ml.v3.z;
351 
352         nd->mTransformation.a4 = ml.off.x;
353         nd->mTransformation.b4 = ml.off.y;
354         nd->mTransformation.c4 = ml.off.z;
355 
356         nodes.push_back(nd);
357 
358         GeData data;
359         if (type == Ocamera)
360         {
361             object->GetParameter(CAMERAOBJECT_FOV, data);
362             // TODO: read camera
363         }
364         else if (type == Olight)
365         {
366             // TODO: read light
367         }
368         else if (type == Opolygon)
369         {
370             aiMesh* const mesh = ReadMesh(object);
371             if(mesh != NULL) {
372                 nd->mNumMeshes = 1;
373                 nd->mMeshes = new unsigned int[1];
374                 nd->mMeshes[0] = static_cast<unsigned int>(meshes.size());
375                 meshes.push_back(mesh);
376             }
377         }
378         else {
379             LogWarn("ignoring object: " + std::string(GetObjectTypeName(type)));
380         }
381 
382         RecurseHierarchy(object->GetDown(), nd);
383         object = object->GetNext();
384     }
385 
386     // copy nodes over to parent
387     parent->mNumChildren = static_cast<unsigned int>(nodes.size());
388     parent->mChildren = new aiNode*[parent->mNumChildren]();
389     std::copy(nodes.begin(), nodes.end(), parent->mChildren);
390 }
391 
392 
393 // ------------------------------------------------------------------------------------------------
ReadMesh(BaseObject * object)394 aiMesh* C4DImporter::ReadMesh(BaseObject* object)
395 {
396     assert(object != NULL && object->GetType() == Opolygon);
397 
398     // based on Melange sample code
399     PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
400     ai_assert(polyObject != NULL);
401 
402     const LONG pointCount = polyObject->GetPointCount();
403     const LONG polyCount = polyObject->GetPolygonCount();
404     if(!polyObject || !pointCount) {
405         LogWarn("ignoring mesh with zero vertices or faces");
406         return NULL;
407     }
408 
409     const Vector* points = polyObject->GetPointR();
410     ai_assert(points != NULL);
411 
412     const CPolygon* polys = polyObject->GetPolygonR();
413     ai_assert(polys != NULL);
414 
415     ScopeGuard<aiMesh> mesh(new aiMesh());
416     mesh->mNumFaces = static_cast<unsigned int>(polyCount);
417     aiFace* face = mesh->mFaces = new aiFace[mesh->mNumFaces]();
418 
419     mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
420     mesh->mMaterialIndex = 0;
421 
422     unsigned int vcount = 0;
423 
424     // first count vertices
425     for (LONG i = 0; i < polyCount; i++)
426     {
427         vcount += 3;
428 
429         // TODO: do we also need to handle lines or points with similar checks?
430         if (polys[i].c != polys[i].d)
431         {
432             mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
433             ++vcount;
434         }
435     }
436 
437     ai_assert(vcount > 0);
438 
439     mesh->mNumVertices = vcount;
440     aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
441     aiVector3D* normals, *uvs, *tangents, *bitangents;
442     unsigned int n = 0;
443 
444     // check if there are normals, tangents or UVW coordinates
445     BaseTag* tag = object->GetTag(Tnormal);
446     NormalTag* normals_src = NULL;
447     if(tag) {
448         normals_src = dynamic_cast<NormalTag*>(tag);
449         normals = mesh->mNormals = new aiVector3D[mesh->mNumVertices]();
450     }
451 
452     tag = object->GetTag(Ttangent);
453     TangentTag* tangents_src = NULL;
454     if(tag) {
455         tangents_src = dynamic_cast<TangentTag*>(tag);
456         tangents = mesh->mTangents = new aiVector3D[mesh->mNumVertices]();
457         bitangents = mesh->mBitangents = new aiVector3D[mesh->mNumVertices]();
458     }
459 
460     tag = object->GetTag(Tuvw);
461     UVWTag* uvs_src = NULL;
462     if(tag) {
463         uvs_src = dynamic_cast<UVWTag*>(tag);
464         uvs = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]();
465     }
466 
467     // copy vertices and extra channels over and populate faces
468     for (LONG i = 0; i < polyCount; ++i, ++face)
469     {
470         ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
471         const Vector& pointA = points[polys[i].a];
472         verts->x = pointA.x;
473         verts->y = pointA.y;
474         verts->z = pointA.z;
475         ++verts;
476 
477         ai_assert(polys[i].b < pointCount && polys[i].b >= 0);
478         const Vector& pointB = points[polys[i].b];
479         verts->x = pointB.x;
480         verts->y = pointB.y;
481         verts->z = pointB.z;
482         ++verts;
483 
484         ai_assert(polys[i].c < pointCount && polys[i].c >= 0);
485         const Vector& pointC = points[polys[i].c];
486         verts->x = pointC.x;
487         verts->y = pointC.y;
488         verts->z = pointC.z;
489         ++verts;
490 
491         // TODO: do we also need to handle lines or points with similar checks?
492         if (polys[i].c != polys[i].d)
493         {
494             ai_assert(polys[i].d < pointCount && polys[i].d >= 0);
495 
496             face->mNumIndices = 4;
497             mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
498             const Vector& pointD = points[polys[i].d];
499             verts->x = pointD.x;
500             verts->y = pointD.y;
501             verts->z = pointD.z;
502             ++verts;
503         }
504         else {
505             face->mNumIndices = 3;
506         }
507         face->mIndices = new unsigned int[face->mNumIndices];
508         for(unsigned int j = 0; j < face->mNumIndices; ++j) {
509             face->mIndices[j] = n++;
510         }
511 
512         // copy normals
513         if (normals_src) {
514             if(i >= normals_src->GetDataCount()) {
515                 LogError("unexpected number of normals, ignoring");
516             }
517             else {
518                 ConstNormalHandle normal_handle = normals_src->GetDataAddressR();
519                 NormalStruct nor;
520                 NormalTag::Get(normal_handle, i, nor);
521                 normals->x = nor.a.x;
522                 normals->y = nor.a.y;
523                 normals->z = nor.a.z;
524                 ++normals;
525 
526                 normals->x = nor.b.x;
527                 normals->y = nor.b.y;
528                 normals->z = nor.b.z;
529                 ++normals;
530 
531                 normals->x = nor.c.x;
532                 normals->y = nor.c.y;
533                 normals->z = nor.c.z;
534                 ++normals;
535 
536                 if(face->mNumIndices == 4) {
537                     normals->x = nor.d.x;
538                     normals->y = nor.d.y;
539                     normals->z = nor.d.z;
540                     ++normals;
541                 }
542             }
543         }
544 
545         // copy tangents and bitangents
546         if (tangents_src) {
547 
548             for(unsigned int k = 0; k < face->mNumIndices; ++k) {
549                 LONG l;
550                 switch(k) {
551                 case 0:
552                     l = polys[i].a;
553                     break;
554                 case 1:
555                     l = polys[i].b;
556                     break;
557                 case 2:
558                     l = polys[i].c;
559                     break;
560                 case 3:
561                     l = polys[i].d;
562                     break;
563                 default:
564                     ai_assert(false);
565                 }
566                 if(l >= tangents_src->GetDataCount()) {
567                     LogError("unexpected number of tangents, ignoring");
568                     break;
569                 }
570 
571                 Tangent tan = tangents_src->GetDataR()[l];
572                 tangents->x = tan.vl.x;
573                 tangents->y = tan.vl.y;
574                 tangents->z = tan.vl.z;
575                 ++tangents;
576 
577                 bitangents->x = tan.vr.x;
578                 bitangents->y = tan.vr.y;
579                 bitangents->z = tan.vr.z;
580                 ++bitangents;
581             }
582         }
583 
584         // copy UVs
585         if (uvs_src) {
586             if(i >= uvs_src->GetDataCount()) {
587                 LogError("unexpected number of UV coordinates, ignoring");
588             }
589             else {
590                 UVWStruct uvw;
591                 uvs_src->Get(uvs_src->GetDataAddressR(),i,uvw);
592 
593                 uvs->x = uvw.a.x;
594                 uvs->y = 1.0f-uvw.a.y;
595                 uvs->z = uvw.a.z;
596                 ++uvs;
597 
598                 uvs->x = uvw.b.x;
599                 uvs->y = 1.0f-uvw.b.y;
600                 uvs->z = uvw.b.z;
601                 ++uvs;
602 
603                 uvs->x = uvw.c.x;
604                 uvs->y = 1.0f-uvw.c.y;
605                 uvs->z = uvw.c.z;
606                 ++uvs;
607 
608                 if(face->mNumIndices == 4) {
609                     uvs->x = uvw.d.x;
610                     uvs->y = 1.0f-uvw.d.y;
611                     uvs->z = uvw.d.z;
612                     ++uvs;
613                 }
614             }
615         }
616     }
617 
618     mesh->mMaterialIndex = ResolveMaterial(polyObject);
619     return mesh.dismiss();
620 }
621 
622 
623 // ------------------------------------------------------------------------------------------------
ResolveMaterial(PolygonObject * obj)624 unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
625 {
626     ai_assert(obj != NULL);
627 
628     const unsigned int mat_count = static_cast<unsigned int>(materials.size());
629 
630     BaseTag* tag = obj->GetTag(Ttexture);
631     if(tag == NULL) {
632         return mat_count;
633     }
634 
635     TextureTag& ttag = dynamic_cast<TextureTag&>(*tag);
636 
637     BaseMaterial* const mat = ttag.GetMaterial();
638     assert(mat != NULL);
639 
640     const MaterialMap::const_iterator it = material_mapping.find(mat);
641     if(it == material_mapping.end()) {
642         return mat_count;
643     }
644 
645     ai_assert((*it).second < mat_count);
646     return (*it).second;
647 }
648 
649 #endif // ASSIMP_BUILD_NO_C4D_IMPORTER
650 
651