summaryrefslogtreecommitdiff
path: root/libs/assimp/code/AssetLib/C4D/C4DImporter.cpp
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
committersanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
commitdb81b925d776103326128bf629cbdda576a223e7 (patch)
tree58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/C4D/C4DImporter.cpp
parent55860037b14fb3893ba21cf2654c83d349cc1082 (diff)
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/C4D/C4DImporter.cpp')
-rw-r--r--libs/assimp/code/AssetLib/C4D/C4DImporter.cpp620
1 files changed, 620 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/C4D/C4DImporter.cpp b/libs/assimp/code/AssetLib/C4D/C4DImporter.cpp
new file mode 100644
index 0000000..06d7a34
--- /dev/null
+++ b/libs/assimp/code/AssetLib/C4D/C4DImporter.cpp
@@ -0,0 +1,620 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2021, 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.
+
+#ifndef _MSC_VER
+# error C4D support is currently MSVC only
+#endif
+
+#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() {
+ static auto prefix = "C4D: ";
+ return prefix;
+ }
+}
+
+static const aiImporterDesc desc = {
+ "Cinema4D Importer",
+ "",
+ "",
+ "",
+ aiImporterFlags_SupportBinaryFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "c4d"
+};
+
+
+// ------------------------------------------------------------------------------------------------
+C4DImporter::C4DImporter()
+: BaseImporter() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+C4DImporter::~C4DImporter() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+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 LONG 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 LONG pointCount = polyObject->GetPointCount();
+ const LONG 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 (LONG 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 (LONG 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) {
+ LONG 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