Belle II Software development
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
30using namespace Belle2;
31
32REG_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
186void 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
221void 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
237void 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
283void 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
295void 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
421void 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
481void 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
616void 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
845void 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
936void 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
965void 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
974void 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
983void 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
990void 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
997void 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.
1006HepPolyhedron* 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
G4VPhysicalVolume * getTopVolume()
Return a pointer to the top volume.
static GeometryManager & getInstance()
Return a reference to the instance.
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
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.