Skip to content

Commit 3b814e9

Browse files
committed
ENH: Improve scene exporters and STL, OBJ, and PLY file IO
- Allow reading/writing custom comments in STL, PLY, and OBJ files. This allows storing metadata, such as coordinate system, unit, or color in the files. - Allow reading/writing binary comments (that can contain 0 character) for STL files. This allows reading/writing color information in Mimics-style (Mimics software writes RGBA color as 4 bytes in STL file binary header). - Allow specifying renderer for exporting: Some exporters rejected to export a scene when more renderers were associated with a render window. This was an issue because it prevented scene export when additional renderers were used for displaying various annotations on the render window. Instead of hardcoding using the first renderer, added a ActiveRenderer member, which defines which renderer content should be exported. If not set then the first renderer is used, so the behavior is backward-compatible. - Fixed writing of of .mtl file path in .obj file (full path of .mtl file was written into the .obj file).
1 parent 6699f3c commit 3b814e9

30 files changed

+505
-119
lines changed

IO/Export/Testing/Cxx/UnitTestRIB.cxx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,14 +343,13 @@ int TestRIBExporter()
343343

344344
vtkSmartPointer<vtkRenderer> ren2 =
345345
vtkSmartPointer<vtkRenderer>::New ();
346-
renWin->AddRenderer(ren2);
347346
exporter->SetFilePrefix("dummy");
347+
exporter->SetActiveRenderer(ren2);
348348
exporter->Update();
349-
status += errorObserver->CheckErrorMessage("RIB files only support one renderer per window");
349+
status += errorObserver->CheckErrorMessage("ActiveRenderer must be a renderer owned by the RenderWindow");
350350

351-
renWin->RemoveRenderer(ren2);
352-
ren1->RemoveActor(sphere);
353-
ren1->RemoveActor(strip);
351+
renWin->AddRenderer(ren2);
352+
exporter->SetActiveRenderer(ren2);
354353
exporter->Update();
355354
status += errorObserver->CheckErrorMessage("No actors found for writing .RIB file");
356355

IO/Export/module.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@ vtk_module(vtkIOExport
3838
vtkIOImage
3939
vtkImagingCore
4040
vtklibharu
41+
vtksys
4142
)

IO/Export/vtkExporter.cxx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@
1515
#include "vtkExporter.h"
1616

1717
#include "vtkRenderWindow.h"
18+
#include "vtkRenderer.h"
1819

1920

2021
vtkCxxSetObjectMacro(vtkExporter,RenderWindow,vtkRenderWindow);
22+
vtkCxxSetObjectMacro(vtkExporter,ActiveRenderer,vtkRenderer);
2123

2224

2325
// Construct with no start and end write methods or arguments.
2426
vtkExporter::vtkExporter()
2527
{
2628
this->RenderWindow = nullptr;
29+
this->ActiveRenderer = nullptr;
2730
this->StartWrite = nullptr;
2831
this->StartWriteArgDelete = nullptr;
2932
this->StartWriteArg = nullptr;
@@ -35,6 +38,7 @@ vtkExporter::vtkExporter()
3538
vtkExporter::~vtkExporter()
3639
{
3740
this->SetRenderWindow(nullptr);
41+
this->SetActiveRenderer(nullptr);
3842

3943
if ((this->StartWriteArg)&&(this->StartWriteArgDelete))
4044
{
@@ -57,6 +61,12 @@ void vtkExporter::Write()
5761
vtkErrorMacro(<< "No render window provided!");
5862
return;
5963
}
64+
if ( this->ActiveRenderer != nullptr
65+
&& !this->RenderWindow->HasRenderer(this->ActiveRenderer) )
66+
{
67+
vtkErrorMacro(<< "ActiveRenderer must be a renderer owned by the RenderWindow");
68+
return;
69+
}
6070

6171
if ( this->StartWrite )
6272
{
@@ -144,6 +154,16 @@ void vtkExporter::PrintSelf(ostream& os, vtkIndent indent)
144154
os << indent << "Render Window: (none)\n";
145155
}
146156

157+
if ( this->ActiveRenderer )
158+
{
159+
os << indent << "Active Renderer: (" <<
160+
static_cast<void *>(this->ActiveRenderer) << ")\n";
161+
}
162+
else
163+
{
164+
os << indent << "Active Renderer: (none)\n";
165+
}
166+
147167
if ( this->StartWrite )
148168
{
149169
os << indent << "Start Write: (" <<

IO/Export/vtkExporter.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "vtkIOExportModule.h" // For export macro
4343
#include "vtkObject.h"
4444
class vtkRenderWindow;
45+
class vtkRenderer;
4546

4647
class VTKIOEXPORT_EXPORT vtkExporter : public vtkObject
4748
{
@@ -68,6 +69,21 @@ class VTKIOEXPORT_EXPORT vtkExporter : public vtkObject
6869
vtkGetObjectMacro(RenderWindow,vtkRenderWindow);
6970
//@}
7071

72+
//@{
73+
/**
74+
* Set/Get the renderer that contains actors to be written.
75+
* If it is set to nullptr (by default), then in most subclasses
76+
* the behavior is to only export actors of the first renderer.
77+
* In some subclasses, if ActiveRenderer is nullptr then
78+
* actors of all renderers will be exported.
79+
* The renderer must be in the renderer collection of the specified
80+
* RenderWindow.
81+
* \sa SetRenderWindow()
82+
*/
83+
virtual void SetActiveRenderer(vtkRenderer*);
84+
vtkGetObjectMacro(ActiveRenderer,vtkRenderer);
85+
//@}
86+
7187
//@{
7288
/**
7389
* These methods are provided for backward compatibility. Will disappear
@@ -109,6 +125,7 @@ class VTKIOEXPORT_EXPORT vtkExporter : public vtkObject
109125
~vtkExporter() override;
110126

111127
vtkRenderWindow *RenderWindow;
128+
vtkRenderer *ActiveRenderer;
112129
virtual void WriteData() = 0;
113130

114131
void (*StartWrite)(void *);

IO/Export/vtkIVExporter.cxx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
#include "vtkPolyData.h"
2929
#include "vtkPolyDataMapper.h"
3030
#include "vtkProperty.h"
31-
#include "vtkRenderWindow.h"
3231
#include "vtkRendererCollection.h"
32+
#include "vtkRenderWindow.h"
3333
#include "vtkTexture.h"
3434
#include "vtkTransform.h"
3535
#include "vtkUnsignedCharArray.h"
@@ -57,7 +57,6 @@ static int indent_now = 0;
5757

5858
void vtkIVExporter::WriteData()
5959
{
60-
vtkRenderer *ren;
6160
FILE *fp;
6261
vtkActorCollection *ac;
6362
vtkActor *anActor, *aPart;
@@ -79,16 +78,13 @@ void vtkIVExporter::WriteData()
7978
return;
8079
}
8180

82-
// first make sure there is only one renderer in this rendering window
83-
if (this->RenderWindow->GetRenderers()->GetNumberOfItems() > 1)
81+
// get the renderer
82+
vtkRenderer *ren = this->ActiveRenderer;
83+
if (!ren)
8484
{
85-
vtkErrorMacro(<< "OpenInventor files only support one renderer per window.");
86-
return;
85+
ren = this->RenderWindow->GetRenderers()->GetFirstRenderer();
8786
}
8887

89-
// get the renderer
90-
ren = this->RenderWindow->GetRenderers()->GetFirstRenderer();
91-
9288
// make sure it has at least one actor
9389
if (ren->GetActors()->GetNumberOfItems() < 1)
9490
{

IO/Export/vtkOBJExporter.cxx

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,21 @@
2727
#include "vtkPointData.h"
2828
#include "vtkPolyData.h"
2929
#include "vtkProperty.h"
30-
#include "vtkRenderWindow.h"
3130
#include "vtkRendererCollection.h"
31+
#include "vtkRenderWindow.h"
3232
#include "vtkTransform.h"
33+
#include "vtksys/SystemTools.hxx"
3334

3435
vtkStandardNewMacro(vtkOBJExporter);
3536

3637
vtkOBJExporter::vtkOBJExporter()
3738
{
3839
this->FilePrefix = nullptr;
40+
this->OBJFileComment = nullptr;
41+
this->MTLFileComment = nullptr;
42+
43+
this->SetOBJFileComment("wavefront obj file written by the visualization toolkit");
44+
this->SetMTLFileComment("wavefront mtl file written by the visualization toolkit");
3945
}
4046

4147
vtkOBJExporter::~vtkOBJExporter()
@@ -45,31 +51,19 @@ vtkOBJExporter::~vtkOBJExporter()
4551

4652
void vtkOBJExporter::WriteData()
4753
{
48-
vtkRenderer *ren;
49-
FILE *fpObj, *fpMtl;
50-
vtkActorCollection *ac;
51-
vtkActor *anActor, *aPart;
52-
char nameObj[2048];
53-
char nameMtl[2048];
54-
int idStart = 1;
55-
5654
// make sure the user specified a filename
5755
if ( this->FilePrefix == nullptr)
5856
{
5957
vtkErrorMacro(<< "Please specify file prefix to use");
6058
return;
6159
}
6260

63-
// first make sure there is only one renderer in this rendering window
64-
if (this->RenderWindow->GetRenderers()->GetNumberOfItems() > 1)
61+
vtkRenderer *ren = this->ActiveRenderer;
62+
if (!ren)
6563
{
66-
vtkErrorMacro(<< "obj files only support on renderer per window.");
67-
return;
64+
ren = this->RenderWindow->GetRenderers()->GetFirstRenderer();
6865
}
6966

70-
// get the renderer
71-
ren = this->RenderWindow->GetRenderers()->GetFirstRenderer();
72-
7367
// make sure it has at least one actor
7468
if (ren->GetActors()->GetNumberOfItems() < 1)
7569
{
@@ -78,15 +72,15 @@ void vtkOBJExporter::WriteData()
7872
}
7973

8074
// try opening the files
81-
snprintf(nameObj,sizeof(nameObj),"%s.obj",this->FilePrefix);
82-
snprintf(nameMtl,sizeof(nameMtl),"%s.mtl",this->FilePrefix);
83-
fpObj = fopen(nameObj,"w");
75+
std::string objFilePath = std::string(this->FilePrefix) + ".obj";
76+
FILE *fpObj = fopen(objFilePath.c_str(), "w");
8477
if (!fpObj)
8578
{
8679
vtkErrorMacro(<< "unable to open .obj files ");
8780
return;
8881
}
89-
fpMtl = fopen(nameMtl,"w");
82+
std::string mtlFilePath = std::string(this->FilePrefix) + ".mtl";
83+
FILE *fpMtl = fopen(mtlFilePath.c_str(), "w");
9084
if (!fpMtl)
9185
{
9286
fclose(fpObj);
@@ -98,20 +92,30 @@ void vtkOBJExporter::WriteData()
9892
// Write header
9993
//
10094
vtkDebugMacro("Writing wavefront files");
101-
fprintf(fpObj,
102-
"# wavefront obj file written by the visualization toolkit\n\n");
103-
fprintf(fpObj, "mtllib %s\n\n", nameMtl);
104-
fprintf(fpMtl,
105-
"# wavefront mtl file written by the visualization toolkit\n\n");
106-
107-
ac = ren->GetActors();
108-
vtkAssemblyPath *apath;
109-
vtkCollectionSimpleIterator ait;
110-
for (ac->InitTraversal(ait); (anActor = ac->GetNextActor(ait)); )
95+
if (this->GetOBJFileComment())
96+
{
97+
fprintf(fpObj, "# %s\n\n", this->GetOBJFileComment());
98+
}
99+
100+
std::string mtlFileName = vtksys::SystemTools::GetFilenameName(mtlFilePath);
101+
fprintf(fpObj, "mtllib %s\n\n", mtlFileName.c_str());
102+
103+
if (this->GetMTLFileComment())
111104
{
112-
for (anActor->InitPathTraversal(); (apath=anActor->GetNextPath()); )
105+
fprintf(fpMtl, "# %s\n\n", this->GetMTLFileComment());
106+
}
107+
108+
109+
vtkActorCollection *allActors = ren->GetActors();
110+
vtkCollectionSimpleIterator actorsIt;
111+
vtkActor *anActor;
112+
int idStart = 1;
113+
for (allActors->InitTraversal(actorsIt); (anActor = allActors->GetNextActor(actorsIt)); )
114+
{
115+
vtkAssemblyPath *aPath;
116+
for (anActor->InitPathTraversal(); (aPath = anActor->GetNextPath()); )
113117
{
114-
aPart=static_cast<vtkActor *>(apath->GetLastNode()->GetViewProp());
118+
vtkActor *aPart = vtkActor::SafeDownCast(aPath->GetLastNode()->GetViewProp());
115119
this->WriteAnActor(aPart, fpObj, fpMtl, idStart);
116120
}
117121
}
@@ -383,13 +387,7 @@ void vtkOBJExporter::PrintSelf(ostream& os, vtkIndent indent)
383387
{
384388
this->Superclass::PrintSelf(os,indent);
385389

386-
if (this->FilePrefix)
387-
{
388-
os << indent << "FilePrefix: " << this->FilePrefix << "\n";
389-
}
390-
else
391-
{
392-
os << indent << "FilePrefix: (null)\n";
393-
}
390+
os << indent << "FilePrefix: " << (this->FilePrefix ? this->FilePrefix : "(null)") << "\n";
391+
os << indent << "OBJFileComment: " << (this->OBJFileComment ? this->OBJFileComment : "(null)") << "\n";
392+
os << indent << "MTLFileComment: " << (this->MTLFileComment ? this->MTLFileComment : "(null)") << "\n";
394393
}
395-

IO/Export/vtkOBJExporter.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,31 @@ class VTKIOEXPORT_EXPORT vtkOBJExporter : public vtkExporter
4949
vtkGetStringMacro(FilePrefix);
5050
//@}
5151

52+
//@{
53+
/**
54+
* Specify comment string that will be written to the obj file header.
55+
*/
56+
vtkSetStringMacro(OBJFileComment);
57+
vtkGetStringMacro(OBJFileComment);
58+
//@}
59+
60+
//@{
61+
/**
62+
* Specify comment string that will be written to the mtl file header.
63+
*/
64+
vtkSetStringMacro(MTLFileComment);
65+
vtkGetStringMacro(MTLFileComment);
66+
//@}
67+
5268
protected:
5369
vtkOBJExporter();
5470
~vtkOBJExporter() override;
5571

5672
void WriteData() override;
5773
void WriteAnActor(vtkActor *anActor, FILE *fpObj, FILE *fpMat, int &id);
5874
char *FilePrefix;
75+
char *OBJFileComment;
76+
char *MTLFileComment;
5977
private:
6078
vtkOBJExporter(const vtkOBJExporter&) = delete;
6179
void operator=(const vtkOBJExporter&) = delete;

IO/Export/vtkOOGLExporter.cxx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ static int indent_now = 0;
5757

5858
void vtkOOGLExporter::WriteData()
5959
{
60-
vtkRenderer *ren;
6160
FILE *fp;
6261
int i, j;
6362
vtkActorCollection *ac;
@@ -81,16 +80,12 @@ void vtkOOGLExporter::WriteData()
8180
return;
8281
}
8382

84-
// first make sure there is only one renderer in this rendering window
85-
if (this->RenderWindow->GetRenderers()->GetNumberOfItems() > 1)
83+
vtkRenderer *ren = this->ActiveRenderer;
84+
if (!ren)
8685
{
87-
vtkErrorMacro(<< "Support for only one renderer per window.");
88-
return;
86+
ren = this->RenderWindow->GetRenderers()->GetFirstRenderer();
8987
}
9088

91-
// get the renderer
92-
ren = this->RenderWindow->GetRenderers()->GetFirstRenderer();
93-
9489
// make sure it has at least one actor
9590
if (ren->GetActors()->GetNumberOfItems() < 1)
9691
{

IO/Export/vtkPDFExporter.cxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ void vtkPDFExporter::RenderContextActors()
152152
vtkRenderer *ren;
153153
for (renCol->InitTraversal(renIt); (ren = renCol->GetNextRenderer(renIt));)
154154
{
155+
if (this->ActiveRenderer && ren != this->ActiveRenderer)
156+
{
157+
// If ActiveRenderer is specified then ignore all other renderers
158+
continue;
159+
}
155160
if (ren->GetLayer() == i)
156161
{
157162
vtkPropCollection *props = ren->GetViewProps();

IO/Export/vtkPDFExporter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
* @brief Exports vtkContext2D scenes to PDF.
1818
*
1919
* This exporter draws context2D scenes into a PDF file.
20+
*
21+
* If ActiveRenderer is specified then it exports contents of
22+
* ActiveRenderer. Otherwise it exports contents of all renderers.
2023
*/
2124

2225
#ifndef vtkPDFExporter_h

0 commit comments

Comments
 (0)