"...git@code.ihep.ac.cn:yudian2002/cepcsw-ote-development.git" did not exist on "fe188f7f90e5c6d1cd5950942095091b8a03bd44"
Newer
Older
Markus FRANK
committed
//==========================================================================
// 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/Detector.h>
Markus FRANK
committed
#include <DD4hep/Printout.h>
#include <DDCAD/ASSIMPWriter.h>
#include <DDCAD/Utilities.h>
Markus FRANK
committed
/// Open Asset Importer Library
#include "assimp/postprocess.h"
#include "assimp/Exporter.hpp"
#include "assimp/scene.h"
/// ROOT include files
#include <TBuffer3D.h>
Markus FRANK
committed
#include <TBuffer3DTypes.h>
Markus FRANK
committed
#include <TGeoBoolNode.h>
Markus FRANK
committed
#include <TGeoMatrix.h>
Markus FRANK
committed
/// C/C++ include files
#include <set>
using namespace dd4hep::cad;
using Vertex = Tessellated::Vertex_t;
Markus FRANK
committed
namespace {
void _collect(std::vector<std::pair<dd4hep::PlacedVolume,TGeoHMatrix*> >& cont,
bool recursive, const TGeoHMatrix& to_global, dd4hep::PlacedVolume pv)
Markus FRANK
committed
{
dd4hep::Volume v = pv.volume();
Markus FRANK
committed
for(Int_t i=0; i<v->GetNdaughters(); ++i) {
dd4hep::PlacedVolume p = v->GetNode(i);
dd4hep::Solid sol = p.volume().solid();
bool use = sol->IsA() != TGeoShapeAssembly::Class();
std::unique_ptr<TGeoHMatrix> mother(new TGeoHMatrix(to_global));
Markus FRANK
committed
mother->Multiply(p->GetMatrix());
if ( use ) {
TGeoHMatrix* m = mother.release();
cont.push_back(std::make_pair(p, m));
if ( recursive )
_collect(cont, recursive, *m, p);
}
else if ( recursive ) {
_collect(cont, recursive, *mother, p);
}
Markus FRANK
committed
}
}
bool equals(Vertex const &lhs, Vertex const &rhs) {
constexpr double kTolerance = 1.e-32;
return TMath::Abs(lhs[0] - rhs[0]) < kTolerance &&
TMath::Abs(lhs[1] - rhs[1]) < kTolerance &&
TMath::Abs(lhs[2] - rhs[2]) < kTolerance;
}
struct TessellateShape {
public:
TessellateShape() = default;
virtual ~TessellateShape() = default;
RootCsg::TBaseMesh* make_mesh(TGeoShape* sh) const;
RootCsg::TBaseMesh* collect_composite(TGeoCompositeShape* sh) const;
std::unique_ptr<TGeoTessellated> build_mesh(int id, const std::string& name, TGeoShape* shape);
std::unique_ptr<TGeoTessellated> tessellate_primitive(const std::string& name, dd4hep::Solid solid);
std::unique_ptr<TGeoTessellated> close_tessellated(int id, TGeoShape* shape, int nskip, std::unique_ptr<TGeoTessellated>&& tes);
Markus FRANK
committed
};
std::unique_ptr<TGeoTessellated>
TessellateShape::close_tessellated(int id, TGeoShape* shape, int nskip, std::unique_ptr<TGeoTessellated>&& tes) {
std::string nam = shape->GetName(), typ = "["+std::string(shape->IsA()->GetName())+"]";
nam = nam.substr(0, nam.find("_0x"));
tes->CloseShape(true, true, true);
if ( nskip > 0 ) {
dd4hep::printout(dd4hep::ALWAYS,
"ASSIMPWriter","+++ %3d %-48s %-24s Skipped %3ld/%-4d degenerate facets %4d vertices closed:%s defined:%s",
id, nam.c_str(), typ.c_str(), nskip, tes->GetNfacets(), tes->GetNvertices(),
dd4hep::yes_no(tes->IsClosedBody()), dd4hep::yes_no(tes->IsDefined()));
dd4hep::printout(dd4hep::ALWAYS,
"ASSIMPWriter","+++ %3d %-48s %-24s Tessellated %4d facets %4d vertices closed:%s defined:%s",
id, nam.c_str(), typ.c_str(),
tes->GetNfacets(), tes->GetNvertices(),
dd4hep::yes_no(tes->IsClosedBody()),
dd4hep::yes_no(tes->IsDefined()));
std::cout << std::flush;
return move(tes);
}
std::unique_ptr<TGeoTessellated> TessellateShape::build_mesh(int id, const std::string& name, TGeoShape* shape) {
auto mesh = std::unique_ptr<RootCsg::TBaseMesh>(this->make_mesh(shape));
std::vector<Vertex> vertices;
std::size_t nskip = 0;
vertices.reserve(mesh->NumberOfVertices());
std::map<std::size_t,std::size_t> vtx_index_replacements;
for( size_t ipoint = 0, npoints = mesh->NumberOfVertices(); ipoint < npoints; ++ipoint ) {
long found = -1;
const double* v = mesh->GetVertex(ipoint);
Vertex vtx(v[0], v[1], v[2]);
for(std::size_t i=0; i < vertices.size(); ++i) {
if ( equals(vertices[i],vtx) ) {
vtx_index_replacements[ipoint] = found = i;
break;
}
}
if ( found < 0 ) {
vtx_index_replacements[ipoint] = vertices.size();
vertices.emplace_back(v[0], v[1], v[2]);
}
}
std::size_t vtx_len = vertices.size();
std::unique_ptr<TGeoTessellated> tes = std::make_unique<TGeoTessellated>(name.c_str(), vertices);
for( std::size_t ipoly = 0, npols = mesh->NumberOfPolys(); ipoly < npols; ++ipoly) {
std::size_t npoints = mesh->SizeOfPoly(ipoly);
printout(dd4hep::DEBUG,"ASSIMPWriter","+++ Got polygon with %ld points",npoints);
///
/// 3-vertex polygons automatically translate to GL_TRIANGLES
/// See Kronos documentation to glBegin / glEnd from the glu library:
/// https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glBegin.xml
///
/// Otherwise:
#if 1
/// Apparently this is the correct choice:
///
/// Interprete as FAN: GL_TRIANGLE_FAN
/// One triangle is defined for each vertex presented after the first two vertices.
/// Vertices 1 , n + 1 , and n + 2 define triangle n.
/// N - 2 triangles are drawn.
std::size_t v0 = mesh->GetVertexIndex(ipoly, 0);
std::size_t vv0 = vtx_index_replacements[v0];
for( std::size_t ipoint = 0; ipoint < npoints-2; ++ipoint ) {
std::size_t v1 = mesh->GetVertexIndex(ipoly, ipoint+1);
std::size_t v2 = mesh->GetVertexIndex(ipoly, ipoint+2);
std::size_t vv1 = vtx_index_replacements[v1];
std::size_t vv2 = vtx_index_replacements[v2];
if ( vv0 > vtx_len || vv1 > vtx_len || vv2 > vtx_len ) {
++nskip;
continue;
}
if ( vv0 == vv1 || vv0 == vv2 || vv1 == vv2 ) {
++nskip;
continue;
}
Vertex w[3] = {vertices[vv0],vertices[vv1],vertices[vv2]};
if ( TGeoFacet::CompactFacet(w, 3) < 3 ) {
++nskip;
continue;
}
#if ROOT_VERSION_CODE >= ROOT_VERSION(6,31,1)
bool degenerated = dd4hep::cad::facetIsDegenerated({vertices[vv0],vertices[vv1],vertices[vv2]});
bool degenerated = true;
TGeoFacet f(&vertices, 3, vv0, vv1, vv2);
f.ComputeNormal(degenerated);
if ( degenerated ) {
++nskip;
continue;
}
tes->AddFacet(vv0, vv1, vv2);
}
#else
/// Interprete as STRIP: GL_TRIANGLE_STRIP
/// One triangle is defined for each vertex presented after the first two vertices.
/// For odd n, vertices n, n + 1 , and n + 2 define triangle n.
/// For even n, vertices n + 1 , n, and n + 2 define triangle n.
/// N - 2 triangles are drawn.
for( std::size_t ipoint = 0; ipoint < npoints-2; ++ipoint ) {
vtx_t v0(mesh->GetVertex(mesh->GetVertexIndex(ipoly, ipoint)));
vtx_t v1(mesh->GetVertex(mesh->GetVertexIndex(ipoly, ipoint+1)));
vtx_t v2(mesh->GetVertex(mesh->GetVertexIndex(ipoly, ipoint+2)));
((ipoint%2) == 0) ? tes->AddFacet(v1, v0, v2) : tes->AddFacet(v0, v1, v2);
}
#endif
}
}
return close_tessellated(id, shape, nskip, move(tes));
}
RootCsg::TBaseMesh* TessellateShape::make_mesh(TGeoShape* sh) const {
if (TGeoCompositeShape *shape = dynamic_cast<TGeoCompositeShape *>(sh)) {
return collect_composite(shape);
}
UInt_t flags = TBuffer3D::kCore|TBuffer3D::kBoundingBox|TBuffer3D::kRawSizes|TBuffer3D::kRaw|TBuffer3D::kShapeSpecific;
const TBuffer3D& buffer = sh->GetBuffer3D(flags, kFALSE);
return RootCsg::ConvertToMesh(buffer);
}
RootCsg::TBaseMesh* TessellateShape::collect_composite(TGeoCompositeShape* sh) const {
TGeoBoolNode* node = sh->GetBoolNode();
TGeoShape* left = node->GetLeftShape();
TGeoShape* right = node->GetRightShape();
TGeoHMatrix* glmat = (TGeoHMatrix*)TGeoShape::GetTransform();
UInt_t oper = node->GetBooleanOperator();
TGeoHMatrix copy(*glmat); // keep a copy
// Do not wonder about this logic.
// GetBuffer3D (->make_mesh) uses static variable fgTransform of TGeoShape!
glmat->Multiply(node->GetLeftMatrix());
auto left_mesh = std::unique_ptr<RootCsg::TBaseMesh>(make_mesh(left));
Markus FRANK
committed
glmat->Multiply(node->GetRightMatrix());
auto right_mesh = std::unique_ptr<RootCsg::TBaseMesh>(make_mesh(right));
*glmat = ©
switch (oper) {
case TGeoBoolNode::kGeoUnion:
return RootCsg::BuildUnion(left_mesh.get(), right_mesh.get());
case TGeoBoolNode::kGeoIntersection:
return RootCsg::BuildIntersection(left_mesh.get(), right_mesh.get());
case TGeoBoolNode::kGeoSubtraction:
return RootCsg::BuildDifference(left_mesh.get(), right_mesh.get());
default:
Error("BuildComposite", "Wrong boolean operation code %d\n", oper);
return 0;
}
}
std::unique_ptr<TGeoTessellated> TessellateShape::tessellate_primitive(const std::string& name, dd4hep::Solid solid) {
using vtx_t = Vertex;
const TBuffer3D& buf3D = solid->GetBuffer3D(TBuffer3D::kAll, false);
Markus FRANK
committed
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;
std::size_t i, num_facet = 0;
Markus FRANK
committed
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;
}
Markus FRANK
committed
std::unique_ptr<TGeoTessellated> tes = std::make_unique<TGeoTessellated>(name.c_str(), num_facet);
Markus FRANK
committed
q = buf3D.fPols;
for( i=0, q=buf3D.fPols; i<buf3D.NbPols(); ++i) {
pol = (pol_t*)q;
q += (2+pol->n);
for( int j=0; j < pol->n; j += 2 ) {
Markus Frank
committed
/* ------------------------------------------------------------
// 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]];
Markus FRANK
committed
Markus Frank
committed
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);
}
Markus FRANK
committed
}
}
return tes;
}
Markus FRANK
committed
}
/// Write output file
int ASSIMPWriter::write(const std::string& file_name,
Markus Frank
committed
const std::string& file_type,
const VolumePlacements& places,
bool recursive,
double unit_scale) const
Markus FRANK
committed
{
std::vector<std::pair<PlacedVolume,TGeoHMatrix*> > placements;
int build_mode = ((flags&0x1) != 0) ? 1 : 0;
bool dump_facets = ((flags&0x2) != 0);
std::vector<Material> materials;
TGeoHMatrix toGlobal;
Markus FRANK
committed
for( auto pv : places )
_collect(placements, recursive, toGlobal, pv);
std::size_t num_mesh = placements.size();
Markus FRANK
committed
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;
auto* geo_transform = TGeoShape::GetTransform();
Markus FRANK
committed
for( std::size_t imesh=0; imesh < num_mesh; ++imesh ) {
std::unique_ptr<TGeoHMatrix> trafo(placements[imesh].second);
std::unique_ptr<TGeoTessellated> shape_holder;
Markus FRANK
committed
PlacedVolume pv = placements[imesh].first;
Volume vol = pv.volume();
Solid sol = vol.solid();
Material mat = vol.material();
TessellatedSolid tes = sol;
aiString node_name(vol.name());
Markus FRANK
committed
identity.Clear();
TGeoShape::SetTransform(&identity);
/// If the shape is already tessellated, no need to create another one!
Markus FRANK
committed
if ( !tes.isValid() ) {
TessellateShape helper;
auto* shape = dynamic_cast<TGeoCompositeShape*>(sol.ptr());
if ( build_mode || shape ) { // Always use this method!
auto* paintVol = detector.manager().GetPaintVolume();
detector.manager().SetPaintVolume(vol.ptr());
shape_holder = helper.build_mesh(imesh, vol.name(), sol.ptr());
detector.manager().SetPaintVolume(paintVol);
Markus FRANK
committed
}
Markus FRANK
committed
else {
shape_holder = helper.tessellate_primitive(vol.name(), sol);
Markus FRANK
committed
}
tes = shape_holder.get();
Markus FRANK
committed
}
if ( tes->GetNfacets() == 0 ) {
continue;
}
std::size_t num_vert = 0;
Markus FRANK
committed
for( long j=0, n=tes->GetNfacets(); j < n; ++j )
num_vert += tes->GetFacet(j).GetNvert();
std::size_t index = std::numeric_limits<std::size_t>::max();
for( std::size_t j=0; j<materials.size(); ++j ) {
Markus Frank
committed
index = j;
break;
Markus FRANK
committed
}
}
if ( index > materials.size() ) {
Markus FRANK
committed
auto* ai_mat = new aiMaterial();
index = materials.size();
Markus FRANK
committed
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 ( vol.visAttributes().isValid() ) {
Markus Frank
committed
float cr = 0e0, cg = 0e0, cb = 0e0, ca = 0e0;
vol.visAttributes().argb(ca, cr, cg, cb);
Markus Frank
committed
mesh->mColors[0] = new aiColor4D(cr, cg, cb, ca);
Markus FRANK
committed
}
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;
Markus FRANK
committed
for( long j=0, nvx=0, n=tes->GetNfacets(); j < n; ++j ) {
bool degenerated = false;
const auto& facet = tes->GetFacet(j);
#if ROOT_VERSION_CODE >= ROOT_VERSION(6,31,1)
tmp = tes->FacetComputeNormal(j, degenerated);
#else
Markus FRANK
committed
tmp = facet.ComputeNormal(degenerated);
Markus FRANK
committed
if ( !degenerated && facet.GetNvert() > 0 ) {
Markus Frank
committed
aiFace& face = mesh->mFaces[mesh->mNumFaces];
double u = unit_scale;
Markus FRANK
committed
Markus Frank
committed
face.mIndices = new unsigned int[facet.GetNvert()];
trafo->LocalToMaster(tmp.fVec, norm.fVec);
Markus Frank
committed
face.mNumIndices = 0;
for( long k=0; k < facet.GetNvert(); ++k ) {
#if ROOT_VERSION_CODE >= ROOT_VERSION(6,31,1)
tmp = tes->GetVertex(facet[k]);
#else
Markus Frank
committed
tmp = facet.GetVertex(k);
trafo->LocalToMaster(tmp.fVec, vtx.fVec);
Markus Frank
committed
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);
Markus Frank
committed
++mesh->mNumVertices;
++face.mNumIndices;
++nvx;
}
++mesh->mNumFaces;
if ( dump_facets ) {
const auto* id = face.mIndices;
const auto* vv = mesh->mVertices;
ROOT::Geom::Vertex_t v1(vv[id[0]].x, vv[id[0]].y, vv[id[0]].z);
ROOT::Geom::Vertex_t v2(vv[id[1]].x, vv[id[1]].y, vv[id[1]].z);
ROOT::Geom::Vertex_t v3(vv[id[2]].x, vv[id[2]].y, vv[id[2]].z);
std::string str = dd4hep::cad::streamVertices(v1, v2, v3);
printout(ALWAYS,"ASSIMPWriter","++ Facet %4ld : %s", j, str.c_str());
}
}
else {
printout(ALWAYS,"ASSIMPWriter",
"+++ Found degenerate facet while writing [Should not happen]");
Markus FRANK
committed
}
}
Markus FRANK
committed
/// Check if we have here a valid mesh
if ( 0 == mesh->mNumFaces || 0 == mesh->mNumVertices ) {
if ( mesh->mVertices ) delete [] mesh->mVertices;
mesh->mVertices = nullptr;
Markus FRANK
committed
mesh->mNumVertices = 0;
if ( mesh->mNormals ) delete [] mesh->mNormals;
mesh->mNormals = nullptr;
if ( mesh->mFaces ) delete [] mesh->mFaces;
mesh->mFaces = nullptr;
Markus FRANK
committed
mesh->mNumFaces = 0;
Markus FRANK
committed
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;
}
TGeoShape::SetTransform(geo_transform);
printout(ALWAYS,"ASSIMPWriter","+++ Analysed %ld out of %ld meshes.",
Markus Frank
committed
scene.mNumMeshes, placements.size());
Markus FRANK
committed
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);
Markus FRANK
committed
exporter.Export(&scene, file_type.c_str(), file_name.c_str(), 0, props);
return 1;
}
return 0;
}