Skip to content
Snippets Groups Projects
ASSIMPWriter.cpp 11.3 KiB
Newer Older
//==========================================================================
//  AIDA Detector description implementation 
//--------------------------------------------------------------------------
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
// All rights reserved.
//
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
//
// Author     : M.Frank
//
//==========================================================================

/// Framework include files
#include <DD4hep/Shapes.h>
#include <DD4hep/Objects.h>
#include <DD4hep/Printout.h>
#include <DDCAD/ASSIMPWriter.h>

/// Open Asset Importer Library
#include "assimp/postprocess.h"
#include "assimp/Exporter.hpp"
#include "assimp/scene.h"

#include <TBuffer3D.h>
#include <TGeoMatrix.h>

/// C/C++ include files
#include <set>

using namespace std;
using namespace dd4hep;
using namespace dd4hep::cad;

namespace  {

  void _collect(std::vector<std::pair<PlacedVolume,TGeoHMatrix*> >& cont,
		bool recursive, const TGeoHMatrix& to_global, PlacedVolume pv)
  {
    Volume v = pv.volume();
    for(Int_t i=0; i<v->GetNdaughters(); ++i)  {
      PlacedVolume p = v->GetNode(i);
      Solid sol = p.volume().solid();
      // TessellatedSolid sol = p.volume().solid();
      // if ( sol.isValid() ) cont.push_back(p);
      unique_ptr<TGeoHMatrix> mother(new TGeoHMatrix(to_global));
      mother->Multiply(p->GetMatrix());

      if ( sol->IsA() != TGeoShapeAssembly::Class() )
	cont.push_back(make_pair(p, mother.get()));
      if ( recursive )
	_collect(cont, recursive, *mother, p);	
      if ( sol->IsA() != TGeoShapeAssembly::Class() )
	mother.release();
    }
  }
  struct vertex{ double x,y,z;
    vertex() = default;
    vertex(const vertex& copy) = default;
    vertex(double vx, double vy, double vz) : x(vx), y(vy), z(vz) {}
    vertex(const Tessellated::Vertex_t& v) : x(v.x()), y(v.y()), z(v.z()) {}
    vertex& operator=(const vertex& v) = default;
    vertex& operator=(const Tessellated::Vertex_t& v)
    { x = v.x(); y = v.y(); z = v.z(); return *this;  }
    operator Tessellated::Vertex_t () const
    { return Tessellated::Vertex_t(x,y,z); }
  };
  vertex operator-(const vertex& v1, const vertex& v2)
  {  return vertex(v1.x-v2.x, v1.y-v2.y, v1.z-v2.z);   }
  vertex operator+(const vertex& v1, const vertex& v2)
  {  return vertex(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z);   }
#if 0
      num_facet = tes->GetNfacets();
      int *nn = new int[num_facet];
      bool* flipped = new bool[num_facet];
      for( i=0; i < num_facet; ++i )
	flipped[i] = false, nn[0] = 0;
      for( i=0; i < num_facet; ++i )   {
	for( size_t j= i+1; j < num_facet; ++j)   {
	  bool isneighbour = tes->GetFacet(i).IsNeighbour(tes->GetFacet(j), flipped[j]);
	  if ( isneighbour )  {
	    if ( flipped[i] ) flipped[j] = !flipped[j];
	    ++nn[i];
	    ++nn[j];
	    if ( nn[i] == tes->GetFacet(i).GetNvert() )
	      break;
	  }
 	}
      }
      for( i=0; i < num_facet; ++i )  {
	if ( flipped[i] )  {
	  const_cast<TGeoFacet*>(&tes->GetFacet(i))->Flip();
	}
      }
      delete [] flipped;
      delete [] nn;
#endif
#if 0
   template <typename TGBinder>
   TPlane3 compute_plane(const TGBinder &poly)   {
      TPoint3 plast(poly[poly.Size()-1]);
      TPoint3 pivot;
      TVector3 edge;
      Int_t j;
      for (j = 0; j < poly.Size(); j++) {
         pivot = poly[j];
         edge =  pivot - plast;
         if (!edge.FuzzyZero()) break;
      }
      for (; j < poly.Size(); j++) {
         TVector3 v2 = poly[j] - pivot;
         TVector3 v3 = edge.Cross(v2);
         if (!v3.FuzzyZero())
            return TPlane3(v3,pivot);
      }

      return TPlane3();
   }


	/* ------------------------------------------------------------
	//   Convert quadri-linear facet to 2 tri-linear facets
	//
	//    f1 +---------------+ v2/v3: f0
	//      /                / 
	//     /                /
	//    /                /
	//    +---------------+
	//  v0             v1 v2/v3
	// --------------------------------------------------------- */
	int num_points[3];
	pol_t* pol = (pol_t*)q;
	int s1 = pol->segs[0], s2 = pol->segs[1];
        int ends[4] = {segs[s1]._1,segs[s1]._2,segs[s2]._1,segs[s2]._2};

	q += (2+pol->n);
	if (ends[0] == ends[2]) {
	  numPnts[0] = ends[1];
	  numPnts[1] = ends[0];
	  numPnts[2] = ends[3];
	}
	else if (ends[0] == ends[3]) {
	  numPnts[0] = ends[1];
	  numPnts[1] = ends[0];
	  numPnts[2] = ends[2];
	}
	else if (ends[1] == ends[2]) {
	  numPnts[0] = ends[0];
	  numPnts[1] = ends[1];
	  numPnts[2] = ends[3];
	}
	else {
	  numPnts[0] = ends[0];
	  numPnts[1] = ends[1];
	  numPnts[2] = ends[2];
	}
	Int_t lastAdded = numPnts[2];
	for( int j=pol->n; j != 2; --j )   {
	  ends[0] = segs[pol->segs[j]]._1;
	  ends[1] = segs[pol->segs[j]]._2;
	  if (ends[0] == lastAdded) {
	    lastAdded = ends[1];
	  }
	  else  {
	    lastAdded = ends[0];
	  }
	}

  
#endif
}

/// Write output file
int ASSIMPWriter::write(const std::string& file_name,
			const std::string& file_type,
			const VolumePlacements& places,
			bool   recursive,
			double unit_scale)  const
{
  std::vector<std::pair<PlacedVolume,TGeoHMatrix*> > placements;
  vector<Material>        materials;
  TGeoHMatrix             toGlobal;
    
  for( auto pv : places )
    _collect(placements, recursive, toGlobal, pv);

  size_t num_mesh = placements.size();

  aiScene scene;
  scene.mNumMaterials = 0;
  scene.mNumMeshes    = 0;
  scene.mMeshes       = new aiMesh* [num_mesh];
  scene.mMaterials    = new aiMaterial* [num_mesh];

  aiNode *root        = new aiNode();
  scene.mRootNode     = root;
  root->mName.Set("<STL>");
  root->mNumMeshes    = 0;
  root->mNumChildren  = 0;
  root->mChildren     = new aiNode* [num_mesh];
  root->mMeshes       = 0;//new unsigned int[root->mNumMeshes];

  for( size_t imesh=0; imesh < num_mesh; ++imesh )   {
    unique_ptr<TGeoHMatrix> trafo(placements[imesh].second);
    PlacedVolume     pv  = placements[imesh].first;
    Volume           v   = pv.volume();
    Material         m   = v.material();
    TessellatedSolid tes = v.solid();
    aiString         node_name(v.name());

    //
    unique_ptr<TGeoTessellated> buf;
    if ( !tes.isValid() )   {
      typedef vertex vtx_t;
      unique_ptr<TBuffer3D> buf3D(v.solid()->MakeBuffer3D());
      struct pol_t { int c, n; int segs[1]; } *pol = nullptr;
      struct seg_t { int c, _1, _2;         };
      const  seg_t* segs = (seg_t*)buf3D->fSegs;
      const  vtx_t* vtcs = (vtx_t*)buf3D->fPnts;
      size_t i, num_facet = 0;
      const  Int_t* q;

      for( i=0, q=buf3D->fPols; i<buf3D->NbPols(); ++i, q += (2+pol->n))  {
	pol = (pol_t*)q;
	for( int j=0; j < pol->n-1; ++j ) num_facet += 2;
      }
      
      buf = make_unique<TGeoTessellated>(v.name(), num_facet);
      tes = buf.get();
      q = buf3D->fPols;
      for( i=0, q=buf3D->fPols; i<buf3D->NbPols(); ++i)  {
	q += (2+pol->n);
	for( int j=0; j < pol->n; j += 2 )   {
	  /* ------------------------------------------------------------
	  //   Convert quadri-linear facet to 2 tri-linear facets
	  //
	  //    f1 +---------------+ v2/v3: f0
	  //      /                / 
	  //     /                /
	  //    /                /
	  //    +---------------+
	  //  v0             v1 v2/v3
	  // --------------------------------------------------------- */
	  const int    s1  = pol->segs[j], s2 = pol->segs[(j+1)%pol->n];
	  const int    s[] = { segs[s1]._1, segs[s1]._2, segs[s2]._1, segs[s2]._2 };
	  const vtx_t& v0  = vtcs[s[0]], &v1=vtcs[s[1]], &v2=vtcs[s[2]], &v3=vtcs[s[3]];

          if ( s[0] == s[2] )   {       // Points are ( s[1], s[0], s[3] )
	    tes->AddFacet(v1, v0, v3);
          }
          else if ( s[0] == s[3] )   {  // Points are ( s[1], s[0], s[2] )
	    tes->AddFacet(v1, v0, v2);
	  }
          else if ( s[1] == s[2] )   {  // Points are ( s[0], s[1], s[3] )
	    tes->AddFacet(v0, v1, v3);
	  }
          else   {                      // Points are ( s[0], s[1], s[2] )
	    tes->AddFacet(v0, v1, v2);
	  }
	}
      }
    }
    if ( tes->GetNfacets() == 0 )   {
      continue;
    }
    
    size_t num_vert = 0;
    for( long j=0, n=tes->GetNfacets(); j < n; ++j )
      num_vert += tes->GetFacet(j).GetNvert();

    size_t index = std::numeric_limits<size_t>::max();
    for( size_t j=0; j<materials.size(); ++j )  {
      if( materials[j] == m )   {
	index = j;
	break;
      }
    }
    if ( index > materials.size() )   {
      aiString name(m.name());
      auto* ai_mat = new aiMaterial();
      index = materials.size();
      materials.push_back(m);
      ai_mat->AddProperty(&name, AI_MATKEY_NAME);
      scene.mMaterials[scene.mNumMaterials] = ai_mat;
      ++scene.mNumMaterials;
    }
    aiMesh* mesh = new aiMesh;
    mesh->mName = node_name;
    mesh->mMaterialIndex = index;
    if ( v.visAttributes().isValid() )   {
      float r = 0e0, g = 0e0, b = 0e0, a = 0e0;
      v.visAttributes().argb(a, r, g, b);
      mesh->mColors[0] = new aiColor4D(r, g, b, a);
    }
    mesh->mFaces       = new aiFace[tes->GetNfacets()];
    mesh->mVertices    = new aiVector3D[num_vert];
    mesh->mNormals     = new aiVector3D[num_vert];
    mesh->mTangents    = nullptr;
    mesh->mBitangents  = nullptr;
    mesh->mNumFaces    = 0;
    mesh->mNumVertices = 0;

    for( long j=0, n=tes->GetNfacets(); j < n; ++j )   {
      aiFace& face = mesh->mFaces[j];
      face.mNumIndices = 0;
      face.mIndices = nullptr;
    }
    vertex vtx, tmp, norm;
    for( long j=0, nvx=0, n=tes->GetNfacets(); j < n; ++j )  {
      bool degenerated  = false;
      const auto& facet = tes->GetFacet(j);
      tmp = facet.ComputeNormal(degenerated);
      if ( !degenerated && facet.GetNvert() > 0 )   {
	aiFace& face  = mesh->mFaces[mesh->mNumFaces];
	double  u     = unit_scale;

	face.mIndices = new unsigned int[facet.GetNvert()];
	trafo->LocalToMaster(&tmp.x, &norm.x);
	face.mNumIndices = 0;
	for( long k=0; k < facet.GetNvert(); ++k )  {
	  tmp = facet.GetVertex(k);
	  trafo->LocalToMaster(&tmp.x, &vtx.x);
	  face.mIndices[face.mNumIndices] = nvx;
	  mesh->mNormals[nvx]  = aiVector3D(norm.x, norm.y, norm.z);
	  mesh->mVertices[nvx] = aiVector3D(vtx.x*u,vtx.y*u,vtx.z*u);
	  ++mesh->mNumVertices;
	  ++face.mNumIndices;
	  ++nvx;
	}
	++mesh->mNumFaces;
      }
    }
    if ( imesh == 122585 )
      break;
    else if ( imesh == 122586 )
      mesh->mNumFaces = 0;
    else if ( imesh == 122587 )
      mesh->mNumFaces = 0;

    /// Check if we have here a valid mesh
    if ( 0 == mesh->mNumFaces || 0 == mesh->mNumVertices )    {
      delete [] mesh->mVertices;
      mesh->mNumVertices = 0;
      delete [] mesh->mNormals;
      delete [] mesh->mFaces;
      mesh->mNumFaces = 0;
      continue;
    }
    
    scene.mMeshes[scene.mNumMeshes] = mesh;

    aiNode *node      = new aiNode;
    node->mMeshes     = new unsigned int[node->mNumMeshes=1];
    node->mMeshes[0]  = scene.mNumMeshes;
    node->mParent     = root;
    node->mName.Set("<STL>");

    root->mChildren[root->mNumChildren] = node;
    ++root->mNumChildren;
    ++scene.mNumMeshes;
  }
  printout(ALWAYS,"ASSIMPWriter","+++ Analysed %ld of %ld meshes.",
	   scene.mNumMeshes, placements.size());
  if ( scene.mNumMeshes > 0 )   {
    Assimp::Exporter exporter;
    Assimp::ExportProperties *props = new Assimp::ExportProperties;
    props->SetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS,
			   flags&EXPORT_POINT_CLOUDS ? true : false);
    exporter.Export(&scene, file_type.c_str(), file_name.c_str(), 0, props);
    return 1;
  }
  return 0;
}