Belle II Software  release-08-01-10
FBXWriterModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <geometry/modules/fbxWriter/FBXWriterModule.h>
10 #include <geometry/GeometryManager.h>
11 
12 #include "G4PhysicalVolumeStore.hh"
13 #include "G4LogicalVolumeStore.hh"
14 #include "G4SolidStore.hh"
15 #include "G4VPhysicalVolume.hh"
16 #include "G4LogicalVolume.hh"
17 #include "G4VSolid.hh"
18 #include "G4DisplacedSolid.hh"
19 #include "G4Material.hh"
20 #include "G4VisAttributes.hh"
21 #include "G4ThreeVector.hh"
22 #include "G4RotationMatrix.hh"
23 #include "G4Transform3D.hh"
24 #include "G4VPVParameterisation.hh"
25 #include <G4Tubs.hh>
26 #include <G4Polyhedron.hh>
27 
28 #include <iomanip>
29 
30 using namespace Belle2;
31 
32 REG_MODULE(FBXWriter);
33 
35 {
36  //Set module properties and the description
37  setDescription("Write the detector geometry in a (semi-)hierarchical FBX format.");
38 
39  //Parameter definition
40  addParam("usePrototypes", m_UsePrototypes, "Use LogVol and PhysVol prototypes", false);
41 
42  //Parameter definition
43  addParam("outputFile", m_Filename, "Output filename", std::string("belle2.fbx"));
44 }
45 
47 {
48  m_First = true;
49 }
50 
52 {
53  if (!m_First) return;
54  m_First = false;
55  G4VPhysicalVolume* topVol = geometry::GeometryManager::getInstance().getTopVolume();
56  if (!topVol) {
57  B2ERROR("No geometry found: add the Geometry module to the path before the FBXWriter module.");
58  return;
59  }
60 
61  G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
62  G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
63  G4SolidStore* solidStore = G4SolidStore::GetInstance();
64 
65  // Assign legal and unique names to each used physical volume, logical volume and solid
66  m_PVName = new std::vector<std::string>(pvStore->size(), "");
67  m_LVName = new std::vector<std::string>(lvStore->size(), "");
68  m_SolidName = new std::vector<std::string>(solidStore->size(), "");
69  for (G4VPhysicalVolume* physVol : *pvStore) {
70  int pvIndex = std::find(pvStore->begin(), pvStore->end(), physVol) - pvStore->begin();
71  if ((*m_PVName)[pvIndex].length() == 0) {
72  assignName(m_PVName, pvIndex, physVol->GetName(), 0);
73  G4LogicalVolume* logVol = physVol->GetLogicalVolume();
74  int lvIndex = std::find(lvStore->begin(), lvStore->end(), logVol) - lvStore->begin();
75  if ((*m_LVName)[lvIndex].length() == 0) {
76  assignName(m_LVName, lvIndex, logVol->GetName(), 1);
77  G4VSolid* solid = logVol->GetSolid();
78  int solidIndex = std::find(solidStore->begin(), solidStore->end(), solid) - solidStore->begin();
79  if ((*m_SolidName)[solidIndex].length() == 0) {
80  assignName(m_SolidName, solidIndex, solid->GetName(), 2);
81  }
82  }
83  }
84  }
85 
86  // Count the number of references to each physical volume and logical volume and solid
87  // so that these values can be placed in the FBX file's Definitions{} section.
88  m_PVCount = new std::vector<unsigned int>(pvStore->size(), 0);
89  m_LVCount = new std::vector<unsigned int>(lvStore->size(), 0);
90  m_SolidCount = new std::vector<unsigned int>(solidStore->size(), 0);
91  m_PVReplicas = new std::vector<unsigned int>(pvStore->size(), 0);
92  m_LVReplicas = new std::vector<unsigned int>(lvStore->size(), 0);
93  m_SolidReplicas = new std::vector<unsigned int>(solidStore->size(), 0);
94  m_LVUnique = new std::vector<bool>(lvStore->size(), true);
95  countEntities(topVol);
96  unsigned int geometryCount = 0;
97  unsigned int materialCount = 0;
98  unsigned int modelCount = 0;
99  for (unsigned int pvIndex = 0; pvIndex < pvStore->size(); ++pvIndex) {
100  if ((*m_PVName)[pvIndex].length() > 0) {
101  modelCount += (*m_PVCount)[pvIndex] + (*m_PVReplicas)[pvIndex];
102  }
103  }
104  for (unsigned int lvIndex = 0; lvIndex < lvStore->size(); ++lvIndex) {
105  if ((*m_LVName)[lvIndex].length() > 0) {
106  if (!(*m_LVUnique)[lvIndex]) {
107  modelCount += (*m_LVCount)[lvIndex] + (*m_LVReplicas)[lvIndex];
108  }
109  materialCount++;
110  }
111  }
112  for (unsigned int solidIndex = 0; solidIndex < solidStore->size(); ++solidIndex) {
113  if ((*m_SolidName)[solidIndex].length() > 0) {
114  geometryCount += (*m_SolidCount)[solidIndex] + (*m_SolidReplicas)[solidIndex] + 1;
115  }
116  }
117 
118  m_File.open(m_Filename, std::ios_base::trunc);
119  writePreamble(modelCount, materialCount, geometryCount);
120 
121  // Write all solids as Geometry nodes (replicas are written later).
122  // Write all logical volumes as Material nodes (color information).
123  // Write all physical and logical volumes as Model nodes (with replica-solids treated here).
124  m_PVID = new std::vector<unsigned long long>(pvStore->size(), 0x0000010000000000LL);
125  m_LVID = new std::vector<unsigned long long>(lvStore->size(), 0x000000C000000000LL);
126  m_SolidID = new std::vector<unsigned long long>(solidStore->size(), 0x0000008000000000LL);
127  m_MatID = new std::vector<unsigned long long>(lvStore->size(), 0x0000004000000000LL);
128  m_Visible = new std::vector<bool>(lvStore->size(), false);
129  m_File << "Objects: {" << std::endl;
130  for (unsigned int solidIndex = 0; solidIndex < solidStore->size(); ++solidIndex) {
131  (*m_SolidID)[solidIndex] += 0x0000000001000000LL * solidIndex;
132  if ((*m_SolidName)[solidIndex].length() > 0) {
133  for (unsigned int solidCount = 0; solidCount <= (*m_SolidCount)[solidIndex]; ++solidCount) { // note lower and upper limits!
134  writeGeometryNode((*solidStore)[solidIndex], (*m_SolidName)[solidIndex], (*m_SolidID)[solidIndex] + solidCount);
135  }
136  }
137  }
138  for (unsigned int lvIndex = 0; lvIndex < lvStore->size(); ++lvIndex) {
139  (*m_MatID)[lvIndex] += 0x0000000001000000LL * lvIndex;
140  (*m_LVID)[lvIndex] += 0x0000000001000000LL * lvIndex;
141  if ((*m_LVName)[lvIndex].length() > 0) {
142  if (!(*m_LVUnique)[lvIndex]) writeMaterialNode(lvIndex, (*m_LVName)[lvIndex]);
143  }
144  }
145  for (unsigned int pvIndex = 0; pvIndex < pvStore->size(); ++pvIndex) {
146  (*m_PVID)[pvIndex] += 0x0000000001000000LL * pvIndex;
147  }
148  m_PVCount->assign(pvStore->size(), 0);
149  m_LVCount->assign(lvStore->size(), 0);
150  m_SolidCount->assign(solidStore->size(), 0);
151  addModels(topVol, 0);
152  m_File << "}" << std::endl << std::endl;
153 
154  // Recursively write the connections among the solid and logical/physical volume elements
155  m_PVCount->assign(pvStore->size(), 0);
156  m_LVCount->assign(lvStore->size(), 0);
157  m_SolidCount->assign(solidStore->size(), 0);
158  m_File << "Connections: {" << std::endl;
159  addConnections(topVol, 0);
160  int pvIndex = std::find(pvStore->begin(), pvStore->end(), topVol) - pvStore->begin();
161  m_File << "\t; Physical volume Model::" << (*m_PVName)[pvIndex] << " to Model::RootNode" << std::endl <<
162  "\tC: \"OO\"," << (*m_PVID)[pvIndex] << ",0" << std::endl << std::endl <<
163  "}" << std::endl << std::endl;
164 
165  m_File << "Takes: {" << std::endl <<
166  "\tCurrent: \"\"" << std::endl <<
167  "}" << std::endl;
168 
169  m_File.close();
170  B2INFO("FBX written to " << m_Filename);
171 
172  delete m_PVName;
173  delete m_LVName;
174  delete m_SolidName;
175  delete m_PVID;
176  delete m_LVID;
177  delete m_MatID;
178  delete m_SolidID;
179  delete m_PVCount;
180  delete m_LVCount;
181  delete m_SolidCount;
182  delete m_Visible;
183 
184 }
185 
186 void FBXWriterModule::assignName(std::vector<std::string>* names, unsigned int index, const G4String& originalName, int select)
187 {
188  G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
189  G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
190  G4SolidStore* solidStore = G4SolidStore::GetInstance();
191 
192  G4String name = originalName;
193  if (name.length() == 0) { name = "anonymous"; }
194  // Replace problematic characters with underscore
195  for (char c : " .,:;?'\"*+-=|^!/@#$\\%{}[]()<>") std::replace(name.begin(), name.end(), c, '_');
196  // Avoid duplicate names for entities that will be written to FBX file
197  for (int j = (int)index - 1; j >= 0; --j) {
198  if ((*names)[j].length() == 0) continue;
199  int match = 0;
200  switch (select) {
201  case 0:
202  match = (*pvStore)[j]->GetName().compare((*pvStore)[index]->GetName()); break;
203  case 1:
204  match = (*lvStore)[j]->GetName().compare((*lvStore)[index]->GetName()); break;
205  case 2:
206  match = (*solidStore)[j]->GetName().compare((*solidStore)[index]->GetName()); break;
207  }
208  if (match == 0) {
209  if (name.length() == (*names)[j].length()) {
210  (*names)[j].append("_1");
211  }
212  int n = std::stoi((*names)[j].substr(name.length() + 1), nullptr);
213  name.append("_");
214  name.append(std::to_string(n + 1));
215  break;
216  }
217  }
218  (*names)[index] = name;
219 }
220 
221 void FBXWriterModule::writeGeometryNode(G4VSolid* solid, const std::string& solidName, unsigned long long solidID)
222 {
223  if ((solid->GetEntityType() == "G4IntersectionSolid") ||
224  (solid->GetEntityType() == "G4UnionSolid") ||
225  (solid->GetEntityType() == "G4SubtractionSolid") ||
226  (solid->GetEntityType() == "G4BooleanSolid")) {
227  HepPolyhedron* polyhedron = getBooleanSolidPolyhedron(solid);
228  auto* g4polyhedron = new G4Polyhedron(*polyhedron);
229  writePolyhedron(solid, g4polyhedron, solidName, solidID);
230  delete polyhedron;
231  delete g4polyhedron;
232  } else {
233  writePolyhedron(solid, solid->GetPolyhedron(), solidName, solidID);
234  }
235 }
236 
237 void FBXWriterModule::writeMaterialNode(int lvIndex, const std::string& matName)
238 {
239  G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
240  G4LogicalVolume* logVol = (*lvStore)[lvIndex];
241  unsigned long long matID = (*m_MatID)[lvIndex];
242  G4Color color(0.0, 1.0, 0.0, 0.5); // default is semi-transparent green
243  if ((matName.compare(0, 23, "eclBarrelCrystalLogical") == 0) ||
244  (matName.compare(0, 20, "eclFwdCrystalLogical") == 0) ||
245  (matName.compare(0, 20, "eclBwdCrystalLogical") == 0) ||
246  (matName.compare(0, 24, "eclBarrelCrystalPhysical") == 0) ||
247  (matName.compare(0, 21, "eclFwdCrystalPhysical") == 0) ||
248  (matName.compare(0, 21, "eclBwdCrystalPhysical") == 0)) {
249  color = G4Color(1.0, 0.25, 0.0, 0.7); // orange since ECL crystals have no G4VisAttribute :(
250  }
251  bool visible = true;
252  G4String materialName = logVol->GetMaterial()->GetName();
253  // Hide containers that have vacuum, air or gas
254  if (materialName == "Vacuum") visible = false;
255  if (materialName == "G4_AIR") visible = false;
256  if (materialName == "CDCGas") visible = false;
257  if (materialName == "ColdAir") visible = false;
258  if (materialName == "STR-DryAir") visible = false;
259  if (materialName == "TOPAir") visible = false;
260  if (materialName == "TOPVacuum") visible = false;
261  const G4VisAttributes* visAttr = logVol->GetVisAttributes();
262  if (visAttr) {
263  color = const_cast<G4Color&>(logVol->GetVisAttributes()->GetColor());
264  if (!(visAttr->IsVisible())) visible = false;
265  } else {
266  visible = false;
267  }
268  if (logVol->GetSensitiveDetector() != nullptr) visible = "";
269  (*m_Visible)[lvIndex] = visible;
270  m_File << "\t; Color for LogVol " << logVol->GetName() << std::endl <<
271  "\tMaterial: " << matID << ", \"Material::" << matName << R"(", "" {)" << std::endl <<
272  "\t\tVersion: 102" << std::endl <<
273  "\t\tProperties70: {" << std::endl <<
274  "\t\t\tP: \"ShadingModel\", \"KString\", \"\", \"\", \"phong\"" << std::endl <<
275  "\t\t\tP: \"DiffuseColor\", \"RGBColor\", \"Color\", \"A\"," <<
276  color.GetRed() << "," << color.GetGreen() << "," << color.GetBlue() << std::endl <<
277  "\t\t\tP: \"TransparentColor\", \"RGBColor\", \"Color\", \"A\",1,1,1" << std::endl <<
278  "\t\t\tP: \"TransparencyFactor\", \"double\", \"Number\", \"\"," << (visible ? 1.0 - color.GetAlpha() : 1) << std::endl <<
279  "\t\t}" << std::endl <<
280  "\t}" << std::endl;
281 }
282 
283 void FBXWriterModule::writeLVModelNode(G4LogicalVolume* logVol, const std::string& lvName, unsigned long long lvID)
284 {
285  m_File << "\t; LogVol " << logVol->GetName() << " with solid " << logVol->GetSolid()->GetName() << std::endl <<
286  "\tModel: " << lvID << ", \"Model::lv_" << lvName << R"(", "Null" {)" << std::endl <<
287  "\t\tVersion: 232" << std::endl <<
288  "\t\tProperties70: {" << std::endl <<
289  "\t\t}" << std::endl <<
290  "\t\tShading: T" << std::endl <<
291  "\t\tCulling: \"CullingOff\"" << std::endl <<
292  "\t}" << std::endl;
293 }
294 
295 void FBXWriterModule::addModels(G4VPhysicalVolume* physVol, int replica)
296 {
297  // Descend to the leaves of the tree
298  G4LogicalVolume* logVol = physVol->GetLogicalVolume();
299  for (size_t daughter = 0; daughter < logVol->GetNoDaughters(); ++daughter) {
300  G4VPhysicalVolume* physVolDaughter = logVol->GetDaughter(daughter);
301  for (int j = 0; j < physVolDaughter->GetMultiplicity(); ++j) {
302  addModels(physVolDaughter, j);
303  }
304  }
305 
306  // Write the physical- and logical-volume models as we ascend the recursive tree
307  G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
308  G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
309  int pvIndex = std::find(pvStore->begin(), pvStore->end(), physVol) - pvStore->begin();
310  unsigned long long pvID = (*m_PVID)[pvIndex];
311  unsigned int pvCount = (*m_PVCount)[pvIndex];
312  std::string pvName = (*m_PVName)[pvIndex];
313  int lvIndex = std::find(lvStore->begin(), lvStore->end(), logVol) - lvStore->begin();
314  unsigned long long lvID = (*m_LVID)[lvIndex];
315  unsigned int lvCount = (*m_LVCount)[lvIndex];
316  std::string lvName = (*m_LVName)[lvIndex];
317  if ((*m_LVUnique)[lvIndex]) writeMaterialNode(lvIndex, (*m_PVName)[pvIndex]);
318  if (physVol->IsReplicated()) {
319  G4VSolid* solid = logVol->GetSolid();
320  G4SolidStore* solidStore = G4SolidStore::GetInstance();
321  int solidIndex = std::find(solidStore->begin(), solidStore->end(), solid) - solidStore->begin();
322  unsigned long long solidID = (*m_SolidID)[solidIndex];
323  EAxis axis;
324  G4int nReplicas;
325  G4double width;
326  G4double offset;
327  G4bool consuming;
328  physVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
329  physVol->SetCopyNo(replica);
330  G4VPVParameterisation* physParameterisation = physVol->GetParameterisation();
331  if (physParameterisation) { // parameterised volume
332  G4VSolid* solidReplica = physParameterisation->ComputeSolid(replica, physVol);
333  physParameterisation->ComputeTransformation(replica, physVol);
334  solidReplica->ComputeDimensions(physParameterisation, replica, physVol);
335  if (!(*solidReplica == *solid)) {
336  std::string solidName = (*m_SolidName)[solidIndex];
337  solidName.append("_R");
338  solidName.append(std::to_string(replica));
339  writeGeometryNode(solidReplica, solidName, solidID + 0x00010000 * replica);
340  }
341  if (m_UsePrototypes && (*solidReplica == *solid)) {
342  if ((replica == 0) && (lvCount == 0)) {
343  if (!(*m_LVUnique)[lvIndex]) writeLVModelNode((*lvStore)[lvIndex], lvName, lvID);
344  }
345  } else {
346  // DIVOT lvName.append("_R");
347  // DIVOT lvName.append(std::to_string(replica));
348  // DIVOT writeLVModelNode((*lvStore)[lvIndex], lvName, lvID+0x00010000*replica+lvCount);
349  }
350  pvName.append("_R");
351  pvName.append(std::to_string(replica));
352  writePVModelNode(physVol, pvName, pvID + 0x00010000 * replica + pvCount);
353  } else { // plain replicated volume
354  G4RotationMatrix* originalRotation = physVol->GetRotation();
355  G4ThreeVector translation; // No translation
356  G4RotationMatrix rotation; // No rotation
357  switch (axis) {
358  default:
359  case kXAxis:
360  translation.setX(width * (replica - 0.5 * (nReplicas - 1)));
361  physVol->SetTranslation(translation);
362  break;
363  case kYAxis:
364  translation.setY(width * (replica - 0.5 * (nReplicas - 1)));
365  physVol->SetTranslation(translation);
366  break;
367  case kZAxis:
368  translation.setZ(width * (replica - 0.5 * (nReplicas - 1)));
369  physVol->SetTranslation(translation);
370  break;
371  case kRho:
372  if (solid->GetEntityType() == "G4Tubs") {
373  double originalRMin = ((G4Tubs*)solid)->GetInnerRadius();
374  double originalRMax = ((G4Tubs*)solid)->GetOuterRadius();
375  ((G4Tubs*)solid)->SetInnerRadius(offset + width * replica);
376  ((G4Tubs*)solid)->SetOuterRadius(offset + width * (replica + 1));
377  std::string solidName = (*m_SolidName)[solidIndex];
378  solidName.append("_R");
379  solidName.append(std::to_string(replica));
380  writeGeometryNode(solid, solidName, (*m_SolidID)[solidIndex] + 0x00010000 * replica);
381  ((G4Tubs*)solid)->SetInnerRadius(originalRMin);
382  ((G4Tubs*)solid)->SetOuterRadius(originalRMax);
383  } else if (replica == 0) {
384  B2WARNING("Built-in volumes replicated along radius for " << solid->GetEntityType() <<
385  " (solid " << solid->GetName() << ") are not visualisable.");
386  }
387  break;
388  case kPhi:
389  physVol->SetRotation(&(rotation.rotateZ(-(offset + (replica + 0.5) * width))));
390  break;
391  }
392  if (m_UsePrototypes && !((axis == kRho) && (solid->GetEntityType() == "G4Tubs"))) {
393  if ((replica == 0) && (lvCount == 0)) {
394  if (!(*m_LVUnique)[lvIndex]) writeLVModelNode((*lvStore)[lvIndex], lvName, lvID);
395  }
396  } else {
397  // DIVOT lvName.append("_R");
398  // DIVOT lvName.append(std::to_string(replica));
399  // DIVOT writeLVModelNode((*lvStore)[lvIndex], lvName, lvID+0x00010000*replica+lvCount);
400  }
401  pvName.append("_R");
402  pvName.append(std::to_string(replica));
403  writePVModelNode(physVol, pvName, pvID + 0x00010000 * replica + pvCount);
404  if (axis == kPhi) physVol->SetRotation(originalRotation);
405  }
406  } else {
407  if (m_UsePrototypes) {
408  if (lvCount == 0) {
409  if (!(*m_LVUnique)[lvIndex]) writeLVModelNode((*lvStore)[lvIndex], lvName, lvID);
410  }
411  if (pvCount == 0) writePVModelNode(physVol, pvName, pvID);
412  } else {
413  // DIVOT writeLVModelNode((*lvStore)[lvIndex], lvName, lvID+lvCount);
414  writePVModelNode(physVol, pvName, pvID + pvCount);
415  }
416  (*m_LVCount)[lvIndex]++;
417  (*m_PVCount)[pvIndex]++;
418  }
419 }
420 
421 void FBXWriterModule::countEntities(G4VPhysicalVolume* physVol)
422 {
423  // Descend to the leaves of the tree
424  G4LogicalVolume* logVol = physVol->GetLogicalVolume();
425  for (size_t daughter = 0; daughter < logVol->GetNoDaughters(); ++daughter) {
426  G4VPhysicalVolume* physVolDaughter = logVol->GetDaughter(daughter);
427  for (int j = 0; j < physVolDaughter->GetMultiplicity(); ++j) {
428  countEntities(physVolDaughter);
429  }
430  }
431  // Count replicas and duplicates of each physical and logical volume as well as the unique
432  // versions of replicated solids as we ascend the recursive tree
433  G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
434  G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
435  G4SolidStore* solidStore = G4SolidStore::GetInstance();
436  G4VSolid* solid = logVol->GetSolid();
437  int pvIndex = std::find(pvStore->begin(), pvStore->end(), physVol) - pvStore->begin();
438  int lvIndex = std::find(lvStore->begin(), lvStore->end(), logVol) - lvStore->begin();
439  int solidIndex = std::find(solidStore->begin(), solidStore->end(), solid) - solidStore->begin();
440  if (physVol->IsReplicated()) {
441  EAxis axis;
442  G4int nReplicas;
443  G4double width;
444  G4double offset;
445  G4bool consuming;
446  physVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
447  G4VPVParameterisation* physParameterisation = physVol->GetParameterisation();
448  if (physParameterisation) { // parameterised volume
449  G4VSolid* solidReplica = physParameterisation->ComputeSolid(0, physVol);
450  physParameterisation->ComputeTransformation(0, physVol);
451  solidReplica->ComputeDimensions(physParameterisation, 0, physVol);
452  if (!(*solidReplica == *solid))(*m_SolidReplicas)[solidIndex]++;
453  if (m_UsePrototypes && (*solidReplica == *solid)) {
454  if ((*m_LVReplicas)[lvIndex] > 0)(*m_LVUnique)[lvIndex] = false;
455  (*m_LVReplicas)[lvIndex] = 1;
456  } else {
457  (*m_LVReplicas)[lvIndex]++;
458  }
459  (*m_PVReplicas)[pvIndex]++;
460  } else { // plain replicated volume
461  if ((axis == kRho) && (solid->GetEntityType() == "G4Tubs"))(*m_SolidReplicas)[solidIndex]++;
462  if (m_UsePrototypes && !((axis == kRho) && (solid->GetEntityType() == "G4Tubs"))) {
463  (*m_LVReplicas)[lvIndex] = 1;
464  } else {
465  (*m_LVReplicas)[lvIndex]++;
466  }
467  (*m_PVReplicas)[pvIndex]++;
468  }
469  } else {
470  if ((*m_LVCount)[lvIndex] > 0)(*m_LVUnique)[lvIndex] = false;
471  if (m_UsePrototypes) {
472  (*m_PVCount)[pvIndex] = 1;
473  (*m_LVCount)[lvIndex] = 1;
474  } else {
475  (*m_PVCount)[pvIndex]++;
476  (*m_LVCount)[lvIndex]++;
477  }
478  }
479 }
480 
481 void FBXWriterModule::addConnections(G4VPhysicalVolume* physVol, int replica)
482 {
483  // Write the PhysVolModel-parentLogVolModel connections as we descend the recursive tree.
484  // If the parentLogVol is referenced at most once, use its referencing PhysVol instead.
485  G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
486  G4LogicalVolumeStore* lvStore = G4LogicalVolumeStore::GetInstance();
487  G4SolidStore* solidStore = G4SolidStore::GetInstance();
488  G4LogicalVolume* logVol = physVol->GetLogicalVolume();
489  int pvIndex = std::find(pvStore->begin(), pvStore->end(), physVol) - pvStore->begin();
490  unsigned long long pvID = (*m_PVID)[pvIndex];
491  unsigned int pvCount = (*m_PVCount)[pvIndex];
492  std::string pvName = (*m_PVName)[pvIndex];
493  int lvIndex = std::find(lvStore->begin(), lvStore->end(), logVol) - lvStore->begin();
494  unsigned long long lvID = (*m_LVID)[lvIndex];
495  unsigned int lvCount = (*m_LVCount)[lvIndex];
496  std::string lvName = (*m_LVName)[lvIndex];
497  for (size_t daughter = 0; daughter < logVol->GetNoDaughters(); ++daughter) {
498  G4VPhysicalVolume* physVolDaughter = logVol->GetDaughter(daughter);
499  int pvIndexDaughter = std::find(pvStore->begin(), pvStore->end(), physVolDaughter) - pvStore->begin();
500  unsigned long long pvIDDaughter = (*m_PVID)[pvIndexDaughter];
501  unsigned int pvCountDaughter = (*m_PVCount)[pvIndexDaughter];
502  for (int j = 0; j < physVolDaughter->GetMultiplicity(); ++j) {
503  if (m_UsePrototypes) {
504  if ((replica == 0) && (j == 0) && (lvCount == 0) && (pvCountDaughter == 0)) {
505  if ((*m_LVUnique)[lvIndex]) {
506  writePVToParentPV((*m_PVName)[pvIndexDaughter], pvName, pvIDDaughter, pvID);
507  } else {
508  writePVToParentLV((*m_PVName)[pvIndexDaughter], lvName, pvIDDaughter, lvID);
509  }
510  }
511  } else {
512  //writePVToParentLV((*m_PVName)[pvIndexDaughter], lvName, pvIDDaughter+0x00010000*j+pvCountDaughter, lvID+0x00010000*replica+lvCount);
513  writePVToParentPV((*m_PVName)[pvIndexDaughter], pvName, pvIDDaughter + 0x00010000 * j + pvCountDaughter,
514  pvID + 0x00010000 * replica + pvCount);
515  }
516  addConnections(physVolDaughter, j);
517  }
518  }
519 
520  // Write the Geometry-LogVolModel, Material-LogVolModel and PhysVolModel-LogVolModel
521  // connections as we ascend the recursive tree
522  G4VSolid* solid = logVol->GetSolid();
523  int solidIndex = std::find(solidStore->begin(), solidStore->end(), solid) - solidStore->begin();
524  unsigned long long solidID = (*m_SolidID)[solidIndex];
525  unsigned long long matID = (*m_MatID)[lvIndex];
526  std::string solidName = (*m_SolidName)[solidIndex];
527  if (physVol->IsReplicated()) {
528  pvName.append("_R");
529  pvName.append(std::to_string(replica));
530  EAxis axis;
531  G4int nReplicas;
532  G4double width;
533  G4double offset;
534  G4bool consuming;
535  physVol->GetReplicationData(axis, nReplicas, width, offset, consuming);
536  physVol->SetCopyNo(replica);
537  G4VPVParameterisation* physParameterisation = physVol->GetParameterisation();
538  if (physParameterisation) { // parameterised volume
539  G4VSolid* solidReplica = physParameterisation->ComputeSolid(replica, physVol);
540  physParameterisation->ComputeTransformation(replica, physVol);
541  solidReplica->ComputeDimensions(physParameterisation, replica, physVol);
542  if (!(*solidReplica == *solid)) {
543  solidName.append("_R");
544  solidName.append(std::to_string(replica));
545  solidID += 0x00010000 * replica;
546  }
547  if (m_UsePrototypes && (*solidReplica == *solid)) {
548  if ((replica == 0) && (lvCount == 0)) {
549  if ((*m_LVUnique)[lvIndex]) { // bypass the singleton logical volume
550  writeSolidToPV(pvName, solidName, (*m_Visible)[lvIndex], matID, pvID, solidID);
551  } else {
552  writeSolidToLV(lvName, solidName, (*m_Visible)[lvIndex], matID, lvID, solidID);
553  }
554  }
555  } else {
556  lvName.append("_R");
557  lvName.append(std::to_string(replica));
558  if ((*m_LVUnique)[lvIndex]) { // bypass the singleton logical volume
559  writeSolidToPV(pvName, solidName, (*m_Visible)[lvIndex], matID, pvID + 0x00010000 * replica + pvCount, solidID);
560  } else {
561  writeSolidToLV(lvName, solidName, (*m_Visible)[lvIndex], matID, lvID + 0x00010000 * replica + lvCount, solidID);
562  }
563  }
564  if (!(*m_LVUnique)[lvIndex]) {
565  writeLVToPV(pvName, lvName, pvID + 0x00010000 * replica + pvCount, lvID + 0x00010000 * replica + lvCount);
566  }
567  } else { // plain replicated volume
568  if ((axis == kRho) && (solid->GetEntityType() == "G4Tubs")) {
569  solidName.append("_R");
570  solidName.append(std::to_string(replica));
571  solidID += 0x00010000 * replica;
572  }
573  if (m_UsePrototypes && !((axis == kRho) && (solid->GetEntityType() == "G4Tubs"))) {
574  if ((replica == 0) && (lvCount == 0)) {
575  if ((*m_LVUnique)[lvIndex]) { // bypass the singleton logical volume
576  writeSolidToPV(pvName, solidName, (*m_Visible)[lvIndex], matID, pvID, solidID);
577  } else {
578  writeSolidToLV(lvName, solidName, (*m_Visible)[lvIndex], matID, lvID, solidID);
579  }
580  }
581  } else {
582  lvName.append("_R");
583  lvName.append(std::to_string(replica));
584  if ((*m_LVUnique)[lvIndex]) { // bypass the singleton logical volume
585  writeSolidToPV(pvName, solidName, (*m_Visible)[lvIndex], matID, pvID + 0x00010000 * replica + pvCount, solidID);
586  } else {
587  writeSolidToLV(lvName, solidName, (*m_Visible)[lvIndex], matID, lvID + 0x00010000 * replica + lvCount, solidID);
588  }
589  }
590  if (!(*m_LVUnique)[lvIndex]) {
591  writeLVToPV(pvName, lvName, pvID + 0x00010000 * replica + pvCount, lvID + 0x00010000 * replica + lvCount);
592  }
593  }
594  } else {
595  if (m_UsePrototypes) {
596  if (lvCount == 0) {
597  if ((*m_LVUnique)[lvIndex]) { // bypass the singleton logical volume
598  writeSolidToPV(pvName, solidName, (*m_Visible)[lvIndex], matID, pvID, solidID);
599  } else {
600  writeSolidToLV(lvName, solidName, (*m_Visible)[lvIndex], matID, lvID, solidID);
601  }
602  }
603  if (pvCount == 0) {
604  if (!(*m_LVUnique)[lvIndex]) writeLVToPV(pvName, lvName, pvID, lvID);
605  }
606  } else {
607  //writeSolidToLV(lvName, solidName, (*m_Visible)[lvIndex], matID, lvID+lvCount, solidID);
608  //writeLVToPV(pvName, lvName, pvID+pvCount, lvID+lvCount);
609  writeSolidToPV(pvName, solidName, (*m_Visible)[lvIndex], matID, pvID + pvCount, solidID);
610  }
611  (*m_LVCount)[lvIndex]++;
612  (*m_PVCount)[pvIndex]++;
613  }
614 }
615 
616 void FBXWriterModule::writePreamble(int modelCount, int materialCount, int geometryCount)
617 {
618  std::time_t t = std::time(nullptr);
619  struct tm* now = std::localtime(&t);
620  m_File << "; FBX 7.3.0 project file" << std::endl <<
621  "; Copyright (C) 1997-2010 Autodesk Inc. and/or its licensors." << std::endl <<
622  "; All rights reserved." << std::endl << std::endl <<
623  "FBXHeaderExtension: {" << std::endl <<
624  "\tFBXHeaderVersion: 1003" << std::endl <<
625  "\tFBXVersion: 7300" << std::endl <<
626  "\tCreationTime: \"" << std::put_time(now, "%F %T") << ":000\"" << std::endl <<
627  //"\tCreationTimeStamp: {" << std::endl <<
628  //"\t\tVersion: 1000" << std::endl <<
629  //"\t\tYear: " << now->tm_year + 1900 << std::endl <<
630  //"\t\tMonth: " << now->tm_mon + 1 << std::endl <<
631  //"\t\tDay: " << now->tm_mday << std::endl <<
632  //"\t\tHour: " << now->tm_hour << std::endl <<
633  //"\t\tMinute: " << now->tm_min << std::endl <<
634  //"\t\tSecond: " << now->tm_sec << std::endl <<
635  //"\t\tMillisecond: 0" << std::endl <<
636  //"\t}" << std::endl <<
637  "\tCreator: \"FBX SDK/FBX Plugins version 2013.3\"" << std::endl <<
638  "\tSceneInfo: \"SceneInfo::GlobalInfo\", \"UserData\" {" << std::endl <<
639  "\t\tType: \"UserData\"" << std::endl <<
640  "\t\tVersion: 100" << std::endl <<
641  "\t\tMetaData: {" << std::endl <<
642  "\t\t\tVersion: 100" << std::endl <<
643  "\t\t\tTitle: \"Belle II Detector\"" << std::endl <<
644  "\t\t\tSubject: \"Detector Geometry Model\"" << std::endl <<
645  "\t\t\tAuthor: \"Belle II Collaboration\"" << std::endl <<
646  "\t\t\tKeywords: \"\"" << std::endl <<
647  "\t\t\tRevision: \"\"" << std::endl <<
648  "\t\t\tComment: \"\"" << std::endl <<
649  "\t\t}" << std::endl <<
650  "\t}" << std::endl <<
651  "}" << std::endl << std::endl;
652 
653  m_File << "GlobalSettings: {" << std::endl <<
654  "\tVersion: 1000" << std::endl <<
655  "\tProperties70: {" << std::endl <<
656  "\t\tP: \"UpAxis\", \"int\", \"Integer\", \"\",1" << std::endl <<
657  "\t\tP: \"UpAxisSign\", \"int\", \"Integer\", \"\",1" << std::endl <<
658  "\t\tP: \"FrontAxis\", \"int\", \"Integer\", \"\",2" << std::endl <<
659  "\t\tP: \"FrontAxisSign\", \"int\", \"Integer\", \"\",1" << std::endl <<
660  "\t\tP: \"CoordAxis\", \"int\", \"Integer\", \"\",0" << std::endl <<
661  "\t\tP: \"CoordAxisSign\", \"int\", \"Integer\", \"\",1" << std::endl <<
662  "\t\tP: \"OriginalUpAxis\", \"int\", \"Integer\", \"\",1" << std::endl <<
663  "\t\tP: \"OriginalUpAxisSign\", \"int\", \"Integer\", \"\",1" << std::endl <<
664  "\t\tP: \"UnitScaleFactor\", \"double\", \"Number\", \"\",1" << std::endl <<
665  "\t\tP: \"OriginalUnitScaleFactor\", \"double\", \"Number\", \"\",1" << std::endl <<
666  "\t\tP: \"AmbientColor\", \"ColorRGB\", \"Color\", \"\",1,1,1" << std::endl <<
667  "\t\tP: \"DefaultCamera\", \"KString\", \"\", \"\", \"Producer Perspective\"" << std::endl <<
668  "\t\tP: \"TimeMode\", \"enum\", \"\", \"\",0" << std::endl <<
669  "\t\tP: \"TimeSpanStart\", \"KTime\", \"Time\", \"\",0" << std::endl <<
670  "\t\tP: \"TimeSpanStop\", \"KTime\", \"Time\", \"\",10" << std::endl <<
671  "\t\tP: \"CustomFrameRate\", \"double\", \"Number\", \"\",-1" << std::endl <<
672  "\t}" << std::endl <<
673  "}" << std::endl << std::endl;
674 
675  m_File << "Documents: {" << std::endl <<
676  "\tCount: 1" << std::endl <<
677  "\tDocument: 4000000000, \"\", \"Scene\" {" << std::endl <<
678  "\t\tProperties70: {" << std::endl <<
679  "\t\t\tP: \"SourceObject\", \"object\", \"\", \"\"" << std::endl <<
680  "\t\t\tP: \"ActiveAnimStackName\", \"KString\", \"\", \"\", \"\"" << std::endl <<
681  "\t\t}" << std::endl <<
682  "\t\tRootNode: 0" << std::endl <<
683  "\t}" << std::endl <<
684  "}" << std::endl << std::endl;
685 
686  m_File << "References: {" << std::endl <<
687  "}" << std::endl << std::endl;
688 
689  m_File << "Definitions: {" << std::endl <<
690  "\tVersion: 100" << std::endl <<
691  "\tCount: 4" << std::endl <<
692  "\tObjectType: \"GlobalSettings\" {" << std::endl <<
693  "\t\tCount: 1" << std::endl <<
694  "\t}" << std::endl;
695  m_File << "\tObjectType: \"Model\" {" << std::endl <<
696  "\t\tCount: " << modelCount << std::endl <<
697  "\t\tPropertyTemplate: \"FbxNode\" {" << std::endl <<
698  "\t\t\tProperties70: {" << std::endl <<
699  "\t\t\t\tP: \"QuaternionInterpolate\", \"enum\", \"\", \"\",0" << std::endl <<
700  "\t\t\t\tP: \"RotationOffset\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
701  "\t\t\t\tP: \"RotationPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
702  "\t\t\t\tP: \"ScalingOffset\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
703  "\t\t\t\tP: \"ScalingPivot\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
704  "\t\t\t\tP: \"TranslationActive\", \"bool\", \"\", \"\",0" << std::endl <<
705  "\t\t\t\tP: \"TranslationMin\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
706  "\t\t\t\tP: \"TranslationMax\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
707  "\t\t\t\tP: \"TranslationMinX\", \"bool\", \"\", \"\",0" << std::endl <<
708  "\t\t\t\tP: \"TranslationMinY\", \"bool\", \"\", \"\",0" << std::endl <<
709  "\t\t\t\tP: \"TranslationMinZ\", \"bool\", \"\", \"\",0" << std::endl <<
710  "\t\t\t\tP: \"TranslationMaxX\", \"bool\", \"\", \"\",0" << std::endl <<
711  "\t\t\t\tP: \"TranslationMaxY\", \"bool\", \"\", \"\",0" << std::endl <<
712  "\t\t\t\tP: \"TranslationMaxZ\", \"bool\", \"\", \"\",0" << std::endl <<
713  "\t\t\t\tP: \"RotationOrder\", \"enum\", \"\", \"\",0" << std::endl <<
714  "\t\t\t\tP: \"RotationSpaceForLimitOnly\", \"bool\", \"\", \"\",0" << std::endl <<
715  "\t\t\t\tP: \"RotationStiffnessX\", \"double\", \"Number\", \"\",0" << std::endl <<
716  "\t\t\t\tP: \"RotationStiffnessY\", \"double\", \"Number\", \"\",0" << std::endl <<
717  "\t\t\t\tP: \"RotationStiffnessZ\", \"double\", \"Number\", \"\",0" << std::endl <<
718  "\t\t\t\tP: \"AxisLen\", \"double\", \"Number\", \"\",10" << std::endl <<
719  "\t\t\t\tP: \"PreRotation\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
720  "\t\t\t\tP: \"PostRotation\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
721  "\t\t\t\tP: \"RotationActive\", \"bool\", \"\", \"\",0" << std::endl <<
722  "\t\t\t\tP: \"RotationMin\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
723  "\t\t\t\tP: \"RotationMax\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
724  "\t\t\t\tP: \"RotationMinX\", \"bool\", \"\", \"\",0" << std::endl <<
725  "\t\t\t\tP: \"RotationMinY\", \"bool\", \"\", \"\",0" << std::endl <<
726  "\t\t\t\tP: \"RotationMinZ\", \"bool\", \"\", \"\",0" << std::endl <<
727  "\t\t\t\tP: \"RotationMaxX\", \"bool\", \"\", \"\",0" << std::endl <<
728  "\t\t\t\tP: \"RotationMaxY\", \"bool\", \"\", \"\",0" << std::endl <<
729  "\t\t\t\tP: \"RotationMaxZ\", \"bool\", \"\", \"\",0" << std::endl <<
730  "\t\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",0" << std::endl <<
731  "\t\t\t\tP: \"ScalingActive\", \"bool\", \"\", \"\",0" << std::endl <<
732  "\t\t\t\tP: \"ScalingMin\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
733  "\t\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
734  "\t\t\t\tP: \"ScalingMinX\", \"bool\", \"\", \"\",0" << std::endl <<
735  "\t\t\t\tP: \"ScalingMinY\", \"bool\", \"\", \"\",0" << std::endl <<
736  "\t\t\t\tP: \"ScalingMinZ\", \"bool\", \"\", \"\",0" << std::endl <<
737  "\t\t\t\tP: \"ScalingMaxX\", \"bool\", \"\", \"\",0" << std::endl <<
738  "\t\t\t\tP: \"ScalingMaxY\", \"bool\", \"\", \"\",0" << std::endl <<
739  "\t\t\t\tP: \"ScalingMaxZ\", \"bool\", \"\", \"\",0" << std::endl <<
740  "\t\t\t\tP: \"GeometricTranslation\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
741  "\t\t\t\tP: \"GeometricRotation\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
742  "\t\t\t\tP: \"GeometricScaling\", \"Vector3D\", \"Vector\", \"\",1,1,1" << std::endl <<
743  "\t\t\t\tP: \"MinDampRangeX\", \"double\", \"Number\", \"\",0" << std::endl <<
744  "\t\t\t\tP: \"MinDampRangeY\", \"double\", \"Number\", \"\",0" << std::endl <<
745  "\t\t\t\tP: \"MinDampRangeZ\", \"double\", \"Number\", \"\",0" << std::endl <<
746  "\t\t\t\tP: \"MaxDampRangeX\", \"double\", \"Number\", \"\",0" << std::endl <<
747  "\t\t\t\tP: \"MaxDampRangeY\", \"double\", \"Number\", \"\",0" << std::endl <<
748  "\t\t\t\tP: \"MaxDampRangeZ\", \"double\", \"Number\", \"\",0" << std::endl <<
749  "\t\t\t\tP: \"MinDampStrengthX\", \"double\", \"Number\", \"\",0" << std::endl <<
750  "\t\t\t\tP: \"MinDampStrengthY\", \"double\", \"Number\", \"\",0" << std::endl <<
751  "\t\t\t\tP: \"MinDampStrengthZ\", \"double\", \"Number\", \"\",0" << std::endl <<
752  "\t\t\t\tP: \"MaxDampStrengthX\", \"double\", \"Number\", \"\",0" << std::endl <<
753  "\t\t\t\tP: \"MaxDampStrengthY\", \"double\", \"Number\", \"\",0" << std::endl <<
754  "\t\t\t\tP: \"MaxDampStrengthZ\", \"double\", \"Number\", \"\",0" << std::endl <<
755  "\t\t\t\tP: \"PreferedAngleX\", \"double\", \"Number\", \"\",0" << std::endl <<
756  "\t\t\t\tP: \"PreferedAngleY\", \"double\", \"Number\", \"\",0" << std::endl <<
757  "\t\t\t\tP: \"PreferedAngleZ\", \"double\", \"Number\", \"\",0" << std::endl <<
758  "\t\t\t\tP: \"LookAtProperty\", \"object\", \"\", \"\"" << std::endl <<
759  "\t\t\t\tP: \"UpVectorProperty\", \"object\", \"\", \"\"" << std::endl <<
760  "\t\t\t\tP: \"Show\", \"bool\", \"\", \"\",1" << std::endl <<
761  "\t\t\t\tP: \"NegativePercentShapeSupport\", \"bool\", \"\", \"\",1" << std::endl <<
762  "\t\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",0" << std::endl <<
763  "\t\t\t\tP: \"Freeze\", \"bool\", \"\", \"\",0" << std::endl <<
764  "\t\t\t\tP: \"LODBox\", \"bool\", \"\", \"\",0" << std::endl <<
765  "\t\t\t\tP: \"Lcl Translation\", \"Lcl Translation\", \"\", \"A\",0,0,0" << std::endl <<
766  "\t\t\t\tP: \"Lcl Rotation\", \"Lcl Rotation\", \"\", \"A\",0,0,0" << std::endl <<
767  "\t\t\t\tP: \"Lcl Scaling\", \"Lcl Scaling\", \"\", \"A\",1,1,1" << std::endl <<
768  "\t\t\t\tP: \"Visibility\", \"Visibility\", \"\", \"A\",1" << std::endl <<
769  "\t\t\t\tP: \"Visibility Inheritance\", \"Visibility Inheritance\", \"\", \"\",1" << std::endl <<
770  "\t\t\t}" << std::endl <<
771  "\t\t}" << std::endl <<
772  "\t}" << std::endl;
773  m_File << "\tObjectType: \"Material\" {" << std::endl <<
774  "\t\tCount: " << materialCount << std::endl <<
775  "\t\tPropertyTemplate: \"FbxSurfacePhong\" {" << std::endl <<
776  "\t\t\tProperties70: {" << std::endl <<
777  "\t\t\t\tP: \"ShadingModel\", \"KString\", \"\", \"\", \"Phong\"" << std::endl <<
778  "\t\t\t\tP: \"MultiLayer\", \"bool\", \"\", \"\",0" << std::endl <<
779  "\t\t\t\tP: \"EmissiveColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
780  "\t\t\t\tP: \"EmissiveFactor\", \"double\", \"Number\", \"A\",0" << std::endl <<
781  "\t\t\t\tP: \"AmbientColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
782  "\t\t\t\tP: \"AmbientFactor\", \"double\", \"Number\", \"A\",0" << std::endl <<
783  "\t\t\t\tP: \"DiffuseColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
784  "\t\t\t\tP: \"DiffuseFactor\", \"double\", \"Number\", \"A\",1" << std::endl <<
785  "\t\t\t\tP: \"Bump\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
786  "\t\t\t\tP: \"NormalMap\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
787  "\t\t\t\tP: \"BumpFactor\", \"double\", \"Number\", \"\",1" << std::endl <<
788  "\t\t\t\tP: \"TransparentColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
789  "\t\t\t\tP: \"TransparencyFactor\", \"double\", \"Number\", \"A\",0" << std::endl <<
790  "\t\t\t\tP: \"DisplacementColor\", \"ColorRGB\", \"Color\", \"\",0,0,0" << std::endl <<
791  "\t\t\t\tP: \"DisplacementFactor\", \"double\", \"Number\", \"\",1" << std::endl <<
792  "\t\t\t\tP: \"VectorDisplacementColor\", \"ColorRGB\", \"Color\", \"\",0,0,0" << std::endl <<
793  "\t\t\t\tP: \"VectorDisplacementFactor\", \"double\", \"Number\", \"\",1" << std::endl <<
794  "\t\t\t\tP: \"SpecularColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
795  "\t\t\t\tP: \"SpecularFactor\", \"double\", \"Number\", \"A\",0" << std::endl <<
796  "\t\t\t\tP: \"ShininessExponent\", \"double\", \"Number\", \"A\",20" << std::endl <<
797  "\t\t\t\tP: \"ReflectionColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
798  "\t\t\t\tP: \"ReflectionFactor\", \"double\", \"Number\", \"A\",0" << std::endl <<
799  "\t\t\t}" << std::endl <<
800  "\t\t}" << std::endl <<
801  "\t}" << std::endl;
802  /*
803  m_File << "\tObjectType: \"Material\" {" << std::endl <<
804  "\t\tCount: " << materialCount << std::endl <<
805  "\t\tPropertyTemplate: \"FbxSurfaceLambert\" {" << std::endl <<
806  "\t\t\tProperties70: {" << std::endl <<
807  "\t\t\t\tP: \"ShadingModel\", \"KString\", \"\", \"\", \"Lambet\"" << std::endl <<
808  "\t\t\t\tP: \"MultiLayer\", \"bool\", \"\", \"\",0" << std::endl <<
809  "\t\t\t\tP: \"EmissiveColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
810  "\t\t\t\tP: \"EmissiveFactor\", \"double\", \"Number\", \"A\",0" << std::endl <<
811  "\t\t\t\tP: \"AmbientColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
812  "\t\t\t\tP: \"AmbientFactor\", \"double\", \"Number\", \"A\",0" << std::endl <<
813  "\t\t\t\tP: \"DiffuseColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
814  "\t\t\t\tP: \"DiffuseFactor\", \"double\", \"Number\", \"A\",1" << std::endl <<
815  "\t\t\t\tP: \"Bump\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
816  "\t\t\t\tP: \"NormalMap\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
817  "\t\t\t\tP: \"BumpFactor\", \"double\", \"Number\", \"\",1" << std::endl <<
818  "\t\t\t\tP: \"TransparentColor\", \"ColorRGB\", \"Color\", \"A\",0,0,0" << std::endl <<
819  "\t\t\t\tP: \"TransparencyFactor\", \"double\", \"Number\", \"A\",0" << std::endl <<
820  "\t\t\t\tP: \"DisplacementColor\", \"ColorRGB\", \"Color\", \"\",0,0,0" << std::endl <<
821  "\t\t\t\tP: \"DisplacementFactor\", \"double\", \"Number\", \"\",1" << std::endl <<
822  "\t\t\t\tP: \"VectorDisplacementColor\", \"ColorRGB\", \"Color\", \"\",0,0,0" << std::endl <<
823  "\t\t\t\tP: \"VectorDisplacementFactor\", \"double\", \"Number\", \"\",1" << std::endl <<
824  "\t\t\t}" << std::endl <<
825  "\t\t}" << std::endl <<
826  "\t}" << std::endl;
827  */
828  m_File << "\tObjectType: \"Geometry\" {" << std::endl <<
829  "\t\tCount: " << geometryCount << std::endl <<
830  "\t\tPropertyTemplate: \"FbxMesh\" {" << std::endl <<
831  "\t\t\tProperties70: {" << std::endl <<
832  "\t\t\t\tP: \"Color\", \"ColorRGB\", \"Color\", \"\",1,1,1" << std::endl <<
833  "\t\t\t\tP: \"BBoxMin\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
834  "\t\t\t\tP: \"BBoxMax\", \"Vector3D\", \"Vector\", \"\",0,0,0" << std::endl <<
835  "\t\t\t\tP: \"Primary Visibility\", \"bool\", \"\", \"\",1" << std::endl <<
836  "\t\t\t\tP: \"Casts Shadows\", \"bool\", \"\", \"\",0" << std::endl <<
837  "\t\t\t\tP: \"Receive Shadows\", \"bool\", \"\", \"\",0" << std::endl <<
838  "\t\t\t}" << std::endl <<
839  "\t\t}" << std::endl <<
840  "\t}" << std::endl <<
841  "}" << std::endl << std::endl;
842 
843 }
844 
845 void FBXWriterModule::writePolyhedron(G4VSolid* solid, G4Polyhedron* polyhedron, const std::string& name,
846  unsigned long long solidID)
847 {
848  if (polyhedron) {
849  polyhedron->SetNumberOfRotationSteps(120);
850  m_File << "\t; Solid " << solid->GetName() << " of type " << solid->GetEntityType() << std::endl <<
851  "\tGeometry: " << solidID << ", \"Geometry::" << name << R"(", "Mesh" {)" << std::endl <<
852  "\t\tVertices: *" << polyhedron->GetNoVertices() * 3 << " {" << std::endl << "\t\t\ta: ";
853  std::streampos startOfLine = m_File.tellp();
854  for (int j = 1; j <= polyhedron->GetNoVertices(); ++j) {
855  m_File << (j == 1 ? "" : ",") <<
856  polyhedron->GetVertex(j).x() << "," <<
857  polyhedron->GetVertex(j).y() << "," <<
858  polyhedron->GetVertex(j).z();
859  if (m_File.tellp() - startOfLine > 100) {
860  startOfLine = m_File.tellp();
861  m_File << std::endl << "\t\t\t\t";
862  }
863  }
864  m_File << std::endl << "\t\t}" << std::endl;
865 
866  std::vector<int> vertices;
867  for (int k = 1; k <= polyhedron->GetNoFacets(); ++k) {
868  G4bool notLastEdge = true;
869  G4int ndx = -1, edgeFlag = 1;
870  do {
871  notLastEdge = polyhedron->GetNextVertexIndex(ndx, edgeFlag);
872  if (notLastEdge) {
873  vertices.push_back(ndx - 1);
874  } else {
875  vertices.push_back(-ndx);
876  }
877  } while (notLastEdge);
878  }
879  m_File << "\t\tPolygonVertexIndex: *" << vertices.size() << " {" << std::endl << "\t\t\ta: ";
880  startOfLine = m_File.tellp();
881  for (unsigned int j = 0; j < vertices.size(); ++j) {
882  m_File << (j == 0 ? "" : ",") << vertices[j];
883  if (m_File.tellp() - startOfLine > 100) {
884  startOfLine = m_File.tellp();
885  m_File << std::endl << "\t\t\t\t";
886  }
887  }
888  m_File << std::endl << "\t\t}" << std::endl;
889 
890  m_File << "\t\tGeometryVersion: 124" << std::endl <<
891  "\t\tLayerElementNormal: 0 {" << std::endl <<
892  "\t\t\tVersion: 101" << std::endl <<
893  // "\t\t\tName: \"\"" << std::endl <<
894  "\t\t\tMappingInformationType: \"ByPolygonVertex\"" << std::endl <<
895  "\t\t\tReferenceInformationType: \"Direct\"" << std::endl <<
896  "\t\t\tNormals: *" << vertices.size() * 3 << " {" << std::endl << "\t\t\t\ta: ";
897  startOfLine = m_File.tellp();
898  unsigned int j = 0;
899  for (int k = 1; k <= polyhedron->GetNoFacets(); ++k) {
900  G4Normal3D normal = polyhedron->GetUnitNormal(k);
901  do {
902  m_File << (j == 0 ? "" : ",") << normal.x() << "," << normal.y() << "," << normal.z();
903  if (m_File.tellp() - startOfLine > 100) {
904  startOfLine = m_File.tellp();
905  m_File << std::endl << "\t\t\t\t";
906  }
907  } while (vertices[j++] >= 0);
908  }
909  m_File << std::endl << "\t\t\t}" << std::endl << "\t\t}" << std::endl <<
910  "\t\tLayerElementMaterial: 0 {" << std::endl <<
911  "\t\t\tVersion: 101" << std::endl <<
912  // "\t\t\tName: \"\"" << std::endl <<
913  "\t\t\tMappingInformationType: \"AllSame\"" << std::endl <<
914  "\t\t\tReferenceInformationType: \"IndexToDirect\"" << std::endl <<
915  "\t\t\tMaterials: *1 {" << std::endl <<
916  "\t\t\t\ta: 0" << std::endl <<
917  "\t\t\t}" << std::endl <<
918  "\t\t}" << std::endl <<
919  "\t\tLayer: 0 {" << std::endl <<
920  "\t\t\tVersion: 100" << std::endl <<
921  "\t\t\tLayerElement: {" << std::endl <<
922  "\t\t\t\tType: \"LayerElementNormal\"" << std::endl <<
923  "\t\t\t\tTypedIndex: 0" << std::endl <<
924  "\t\t\t}" << std::endl <<
925  "\t\t\tLayerElement: {" << std::endl <<
926  "\t\t\t\tType: \"LayerElementMaterial\"" << std::endl <<
927  "\t\t\t\tTypedIndex: 0" << std::endl <<
928  "\t\t\t}" << std::endl <<
929  "\t\t}" << std::endl <<
930  "\t}" << std::endl;
931  } else {
932  B2INFO("Polyhedron representation of solid " << name << " cannot be created");
933  }
934 }
935 
936 void FBXWriterModule::writePVModelNode(G4VPhysicalVolume* physVol, const std::string& pvName, unsigned long long pvID)
937 {
938  G4RotationMatrix* rot = physVol->GetObjectRotation();
939  G4ThreeVector move = physVol->GetObjectTranslation();
940  // FBX uses the Tait-Bryan version of the Euler angles (X then Y then Z rotation)
941  double yaw = std::atan2(rot->yx(), rot->xx()) * 180.0 / M_PI;
942  if (fabs(yaw) < 1.0E-12) yaw = 0.0;
943  double pitch = -std::asin(rot->zx()) * 180.0 / M_PI;
944  if (fabs(pitch) < 1.0E-12) pitch = 0.0;
945  double roll = std::atan2(rot->zy(), rot->zz()) * 180.0 / M_PI;
946  if (fabs(roll) < 1.0E-12) roll = 0.0;
947  m_File << "\t; PhysVol " << physVol->GetName();
948  if (physVol->IsReplicated()) {
949  m_File << " (replicated: copy " << physVol->GetCopyNo() << ")";
950  }
951  m_File << ", placing LogVol " << physVol->GetLogicalVolume()->GetName() << std::endl <<
952  "\tModel: " << pvID << ", \"Model::" << pvName << R"(", "Null" {)" << std::endl <<
953  "\t\tVersion: 232" << std::endl <<
954  "\t\tProperties70: {" << std::endl <<
955  "\t\t\tP: \"Lcl Translation\", \"Lcl Translation\", \"\", \"A\"," <<
956  move.x() << "," << move.y() << "," << move.z() << std::endl <<
957  "\t\t\tP: \"Lcl Rotation\", \"Lcl Rotation\", \"\", \"A\"," <<
958  roll << "," << pitch << "," << yaw << std::endl <<
959  "\t\t}" << std::endl <<
960  "\t\tShading: T" << std::endl <<
961  "\t\tCulling: \"CullingOff\"" << std::endl <<
962  "\t}" << std::endl;
963 }
964 
965 void FBXWriterModule::writeSolidToLV(const std::string& lvName, const std::string& solidName, bool visible,
966  unsigned long long matID, unsigned long long lvID, unsigned long long solidID)
967 {
968  m_File << "\t; Solid Geometry::" << solidName << ", LogVol Model::lv_" << lvName << std::endl <<
969  "\t" << (visible ? "" : "; ") << "C: \"OO\"," << solidID << "," << lvID << std::endl << std::endl <<
970  "\t; Color Material::" << lvName << ", LogVol Model::lv_" << lvName << std::endl <<
971  "\t" << (visible ? "" : "; ") << "C: \"OO\"," << matID << "," << lvID << std::endl << std::endl;
972 }
973 
974 void FBXWriterModule::writeSolidToPV(const std::string& pvName, const std::string& solidName, bool visible,
975  unsigned long long matID, unsigned long long pvID, unsigned long long solidID)
976 {
977  m_File << "\t; Solid Geometry::" << solidName << ", PhysVol Model::" << pvName << std::endl <<
978  "\t" << (visible ? "" : "; ") << "C: \"OO\"," << solidID << "," << pvID << std::endl << std::endl <<
979  "\t; Color Material::" << pvName << ", PhysVol Model::" << pvName << std::endl <<
980  "\t" << (visible ? "" : "; ") << "C: \"OO\"," << matID << "," << pvID << std::endl << std::endl;
981 }
982 
983 void FBXWriterModule::writeLVToPV(const std::string& pvName, const std::string& lvName, unsigned long long pvID,
984  unsigned long long lvID)
985 {
986  m_File << "\t; LogVol Model::lv_" << lvName << ", PhysVol Model::" << pvName << std::endl <<
987  "\tC: \"OO\"," << lvID << "," << pvID << std::endl << std::endl;
988 }
989 
990 void FBXWriterModule::writePVToParentLV(const std::string& pvNameDaughter, const std::string& lvName,
991  unsigned long long pvIDDaughter, unsigned long long lvID)
992 {
993  m_File << "\t; PhysVol Model::" << pvNameDaughter << ", parent LogVol Model::lv_" << lvName << std::endl <<
994  "\tC: \"OO\"," << pvIDDaughter << "," << lvID << std::endl << std::endl;
995 }
996 
997 void FBXWriterModule::writePVToParentPV(const std::string& pvNameDaughter, const std::string& pvName,
998  unsigned long long pvIDDaughter, unsigned long long pvID)
999 {
1000  m_File << "\t; PhysVol Model::" << pvNameDaughter << ", parent PhysVol Model::" << pvName << std::endl <<
1001  "\tC: \"OO\"," << pvIDDaughter << "," << pvID << std::endl << std::endl;
1002 }
1003 
1004 // The code in GEANT4 geometry/solids/Boolean/src/G4BooleanSolid.cc is buggy so avoid it.
1005 // Recursively combine the polyhedrons of two solids to make a resulting polyhedron.
1006 HepPolyhedron* FBXWriterModule::getBooleanSolidPolyhedron(G4VSolid* solid)
1007 {
1008  G4VSolid* solidA = solid->GetConstituentSolid(0);
1009  G4VSolid* solidB = solid->GetConstituentSolid(1);
1010  HepPolyhedron* polyhedronA = nullptr;
1011  if ((solidA->GetEntityType() == "G4IntersectionSolid") ||
1012  (solidA->GetEntityType() == "G4UnionSolid") ||
1013  (solidA->GetEntityType() == "G4SubtractionSolid") ||
1014  (solidA->GetEntityType() == "G4BooleanSolid")) {
1015  polyhedronA = getBooleanSolidPolyhedron(solidA);
1016  } else {
1017  polyhedronA = new HepPolyhedron(*(solidA->GetPolyhedron()));
1018  }
1019  HepPolyhedron* polyhedronB = nullptr;
1020  G4VSolid* solidB2 = solidB;
1021  if (solidB->GetEntityType() == "G4DisplacedSolid") {
1022  solidB2 = ((G4DisplacedSolid*)solidB)->GetConstituentMovedSolid();
1023  }
1024  if ((solidB2->GetEntityType() == "G4IntersectionSolid") ||
1025  (solidB2->GetEntityType() == "G4UnionSolid") ||
1026  (solidB2->GetEntityType() == "G4SubtractionSolid") ||
1027  (solidB2->GetEntityType() == "G4BooleanSolid")) {
1028  polyhedronB = getBooleanSolidPolyhedron(solidB2);
1029  if (solidB != solidB2) { // was solidB a G4DisplacedSolid?
1030  polyhedronB->Transform(G4Transform3D(
1031  ((G4DisplacedSolid*)solidB)->GetObjectRotation(),
1032  ((G4DisplacedSolid*)solidB)->GetObjectTranslation()));
1033  }
1034  } else {
1035  polyhedronB = new HepPolyhedron(*(solidB->GetPolyhedron()));
1036  }
1037  auto* result = new HepPolyhedron();
1038  if (solid->GetEntityType() == "G4UnionSolid") {
1039  *result = polyhedronA->add(*polyhedronB);
1040  } else if (solid->GetEntityType() == "G4SubtractionSolid") {
1041  *result = polyhedronA->subtract(*polyhedronB);
1042  } else if (solid->GetEntityType() == "G4IntersectionSolid") {
1043  *result = polyhedronA->intersect(*polyhedronB);
1044  } else {
1045  B2WARNING("getBooleanSolidPolyhedron(): Unrecognized boolean solid " << solid->GetName() <<
1046  " of type " << solid->GetEntityType());
1047  }
1048  delete polyhedronA;
1049  delete polyhedronB;
1050  return result;
1051 }
std::vector< unsigned int > * m_PVCount
Count of number of instances of each physical volume.
void writePVToParentLV(const std::string &, const std::string &, unsigned long long, unsigned long long)
Write FBX connection for each physical-volume daughter of a parent logical volume.
std::vector< unsigned long long > * m_LVID
Unique identifiers for logical volumes (Model nodes with links to Geometry and Material)
void writeLVToPV(const std::string &, const std::string &, unsigned long long, unsigned long long)
Write FBX connection for the (unique) logical volume of a physical volume.
void addConnections(G4VPhysicalVolume *, int)
Write FBX connections among all of the nodes in the tree (recursive)
FBXWriterModule()
Constructor of the module.
void initialize() override
Initialize at the start of a job.
std::vector< unsigned int > * m_LVReplicas
Count of number of replicas of each logical volume associated with a replicated physical volume.
void event() override
Called for each event: this runs the FBX writer only for the first event.
void addModels(G4VPhysicalVolume *, int)
Process one physical volume for FBX-node writing (recursive)
std::vector< unsigned int > * m_PVReplicas
Count of number of replicas of each replicated physical volume.
std::ofstream m_File
Output file.
void writeSolidToPV(const std::string &, const std::string &, bool, unsigned long long, unsigned long long, unsigned long long)
Write FBX connection for each physical volume's solid and color info (bypass singleton logical volume...
bool m_UsePrototypes
User-specified flag to select whether to write and re-use logical- and physical-volume prototypes onc...
std::vector< unsigned long long > * m_PVID
Unique identifiers for physical volumes (Model nodes with transformation information)
bool m_First
Once-only flag to write FBX only on the first event.
void writePreamble(int, int, int)
Write FBX at the start of the file.
std::vector< bool > * m_Visible
Flag to indicate that the logical volume is visible.
void writePVModelNode(G4VPhysicalVolume *, const std::string &, unsigned long long)
Write FBX definition for each physical volume.
std::vector< bool > * m_LVUnique
Flag to indicate that a logical volume is referenced at most once (eligible for bypass)
void assignName(std::vector< std::string > *, unsigned int, const G4String &, int)
Create unique and legal name for each solid, logical volume, physical volume.
std::vector< unsigned int > * m_SolidCount
Count of number of instances of each solid (typically 1)
void writeLVModelNode(G4LogicalVolume *, const std::string &, unsigned long long)
Write FBX definition for each logical volume.
void countEntities(G4VPhysicalVolume *)
Count the physical volumes, logical volumes, materials and solids (recursive)
std::string m_Filename
User-specified output filename.
std::vector< std::string > * m_PVName
Modified (legal-character and unique) physical-volume name.
std::vector< unsigned int > * m_SolidReplicas
Count of number of replicas of each solid (extras for replicas with modified solids)
std::vector< std::string > * m_SolidName
Modified (legal-character and unique) solid name.
std::vector< unsigned long long > * m_SolidID
Unique identifiers for solids (Geometry nodes)
std::vector< unsigned int > * m_LVCount
Count of number of instances of each logical volume.
std::vector< unsigned long long > * m_MatID
Unique identifiers for logical volumes' color information (Material nodes)
void writeMaterialNode(int, const std::string &)
Write FBX definition for each logical volume's color information.
void writePVToParentPV(const std::string &, const std::string &, unsigned long long, unsigned long long)
Write FBX connection for each physical-volume daughter of a parent physical volume (bypass singleton ...
HepPolyhedron * getBooleanSolidPolyhedron(G4VSolid *)
Create polyhedron for a boolean solid (recursive)
std::vector< std::string > * m_LVName
Modified (legal-character and unique) logical-volume name.
void writeGeometryNode(G4VSolid *, const std::string &, unsigned long long)
Write FBX definition for each solid's polyhedron.
void writePolyhedron(G4VSolid *, G4Polyhedron *, const std::string &, unsigned long long)
Write FBX definition for the solid's polyhedron.
void writeSolidToLV(const std::string &, const std::string &, bool, unsigned long long, unsigned long long, unsigned long long)
Write FBX connection for each logical volume's solid and color info.
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
static GeometryManager & getInstance()
Return a reference to the instance.
G4VPhysicalVolume * getTopVolume()
Return a pointer to the top volume.
REG_MODULE(arichBtest)
Register the Module.
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
Abstract base class for different kinds of events.