Commit e9c97bbf authored by Michael Migliore's avatar Michael Migliore

Add OBJ writer

parent e42e7c4b
......@@ -15,6 +15,7 @@ SET(Module_SRCS
vtkMFIXReader.cxx
vtkMoleculeReaderBase.cxx
vtkOBJReader.cxx
vtkOBJWriter.cxx
vtkOpenFOAMReader.cxx
vtkParticleReader.cxx
vtkPDBReader.cxx
......
......@@ -27,6 +27,7 @@ vtk_add_test_cxx(vtkIOGeometryCxxTests tests
UnstructuredGridCellGradients.cxx
UnstructuredGridFastGradients.cxx
UnstructuredGridGradients.cxx
TestOBJPolyDataWriter.cxx
TestOBJReaderComments.cxx,NO_VALID
TestOBJReaderMaterials.cxx,NO_VALID
TestOBJReaderMultiTexture.cxx,NO_VALID
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestOBJPolyDataWriter.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include <vtkActor.h>
#include <vtkJPEGReader.h>
#include <vtkMath.h>
#include <vtkNew.h>
#include <vtkOBJReader.h>
#include <vtkOBJWriter.h>
#include <vtkPNGReader.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkRegressionTestImage.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkTestUtilities.h>
#include <vtkTexture.h>
#include <vtkTexturedSphereSource.h>
#include <string>
int TestOBJPolyDataWriter(int argc, char* argv[])
{
vtkNew<vtkTexturedSphereSource> sphereSource;
sphereSource->SetThetaResolution(16);
sphereSource->SetPhiResolution(16);
vtkNew<vtkJPEGReader> textReader;
textReader->SetFileName(
vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/NE2_ps_bath_small.jpg"));
std::string tmpDir(
vtkTestUtilities::GetArgOrEnvOrDefault("-T", argc, argv, "VTK_TEMP_DIR", "Testing/Temporary"));
std::string filename = tmpDir + "/TestOBJPolyDataWriter_write.obj";
vtkNew<vtkOBJWriter> writer;
writer->SetFileName(filename.c_str());
writer->SetInputConnection(0, sphereSource->GetOutputPort());
writer->SetInputConnection(1, textReader->GetOutputPort());
writer->Write();
vtkPolyData* polyInput = sphereSource->GetOutput();
// read this file and compare with input
vtkNew<vtkOBJReader> reader;
reader->SetFileName(filename.c_str());
reader->Update();
vtkPolyData* polyOutput = reader->GetOutput();
if (polyInput->GetNumberOfPoints() != polyOutput->GetNumberOfPoints())
{
cerr << "PolyData do not have the same number of points.\n";
return EXIT_FAILURE;
}
vtkDataArray* positionsInput = polyInput->GetPoints()->GetData();
vtkDataArray* positionsOutput = polyOutput->GetPoints()->GetData();
vtkDataArray* normalsInput = polyInput->GetPointData()->GetNormals();
vtkDataArray* normalsOutput = polyOutput->GetPointData()->GetNormals();
vtkDataArray* tcoordsInput = polyInput->GetPointData()->GetTCoords();
vtkDataArray* tcoordsOutput = polyOutput->GetPointData()->GetTCoords();
if (!positionsInput || !positionsOutput || !normalsInput || !normalsOutput || !tcoordsInput ||
!tcoordsOutput)
{
cerr << "One of the arrays is null.\n";
return EXIT_FAILURE;
}
// check values
for (vtkIdType i = 0; i < polyInput->GetNumberOfPoints(); i++)
{
double pi[3], po[3];
// check positions
positionsInput->GetTuple(i, pi);
positionsOutput->GetTuple(i, po);
if (vtkMath::Distance2BetweenPoints(pi, po) > 1e-4)
{
cerr << "One point is different.\n";
cerr << "Input: " << pi[0] << " " << pi[1] << " " << pi[2] << "\n";
cerr << "Output: " << po[0] << " " << po[1] << " " << po[2] << "\n";
return EXIT_FAILURE;
}
// check normals
normalsInput->GetTuple(i, pi);
normalsOutput->GetTuple(i, po);
if (vtkMath::AngleBetweenVectors(pi, po) > 1e-6)
{
cerr << "One normal is different:\n";
cerr << "Input: " << pi[0] << " " << pi[1] << " " << pi[2] << "\n";
cerr << "Output: " << po[0] << " " << po[1] << " " << po[2] << "\n";
return EXIT_FAILURE;
}
// check texture coords
tcoordsInput->GetTuple(i, pi);
tcoordsOutput->GetTuple(i, po);
pi[2] = po[2] = 0.0;
if (vtkMath::Distance2BetweenPoints(pi, po) > 1e-4)
{
cerr << "One texture coord is different:\n";
cerr << "Input: " << pi[0] << " " << pi[1] << "\n";
cerr << "Output: " << po[0] << " " << po[1] << "\n";
return EXIT_FAILURE;
}
}
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(reader->GetOutputPort());
// read png file and set up texture
vtkNew<vtkPNGReader> pngReader;
std::string pngFile = filename.replace(filename.length() - 3, 3, "png");
pngReader->SetFileName(pngFile.c_str());
vtkNew<vtkTexture> texture;
texture->SetInputConnection(pngReader->GetOutputPort());
// add mapper and texture in an actor
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
actor->SetTexture(texture);
// Standard rendering classes
vtkNew<vtkRenderer> renderer;
vtkNew<vtkRenderWindow> renWin;
renWin->AddRenderer(renderer);
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
// set up the view
renderer->SetBackground(0.5, 0.5, 0.5);
renWin->SetSize(300, 300);
renderer->AddActor(actor);
renderer->ResetCamera();
renWin->Render();
int retVal = vtkRegressionTestImage(renWin);
if (retVal == vtkRegressionTester::DO_INTERACTOR)
{
iren->Start();
}
return !retVal;
}
......@@ -20,6 +20,7 @@ vtk_module(vtkIOGeometry
vtkCommonMisc
vtkCommonSystem
vtkCommonTransforms
vtkIOImage
vtksys
vtkzlib
)
/*=========================================================================
Program: Visualization Toolkit
Module: vtkOBJWriter.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkOBJWriter.h"
#include "vtkDataSet.h"
#include "vtkErrorCode.h"
#include "vtkImageData.h"
#include "vtkInformation.h"
#include "vtkPNGWriter.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkSmartPointer.h"
#include "vtkTriangleStrip.h"
#include "vtksys/SystemTools.hxx"
namespace
{
//----------------------------------------------------------------------------
void WriteFaces(std::ofstream& f, vtkCellArray* faces, bool withNormals, bool withTCoords)
{
vtkIdType npts;
vtkIdType* indx;
for (faces->InitTraversal(); faces->GetNextCell(npts, indx);)
{
f << "f";
for (vtkIdType i = 0; i < npts; i++)
{
f << " " << indx[i] + 1;
if (withTCoords)
{
f << "/" << indx[i] + 1;
if (withNormals)
{
f << "/" << indx[i] + 1;
}
}
else if (withNormals)
{
f << "//" << indx[i] + 1;
}
}
f << "\n";
}
}
//----------------------------------------------------------------------------
void WriteLines(std::ofstream& f, vtkCellArray* lines)
{
vtkIdType npts;
vtkIdType* indx;
for (lines->InitTraversal(); lines->GetNextCell(npts, indx);)
{
f << "l";
for (vtkIdType i = 0; i < npts; i++)
{
f << " " << indx[i] + 1;
}
f << "\n";
}
}
//----------------------------------------------------------------------------
void WritePoints(std::ofstream& f, vtkPoints* pts, vtkDataArray* normals, vtkDataArray* tcoords)
{
vtkIdType nbPts = pts->GetNumberOfPoints();
// Positions
for (vtkIdType i = 0; i < nbPts; i++)
{
double p[3];
pts->GetPoint(i, p);
f << "v " << p[0] << " " << p[1] << " " << p[2] << "\n";
}
// Normals
if (normals)
{
for (vtkIdType i = 0; i < nbPts; i++)
{
double p[3];
normals->GetTuple(i, p);
f << "vn " << p[0] << " " << p[1] << " " << p[2] << "\n";
}
}
// Textures
if (tcoords)
{
for (vtkIdType i = 0; i < nbPts; i++)
{
double p[2];
tcoords->GetTuple(i, p);
f << "vt " << p[0] << " " << p[1] << "\n";
}
}
}
//----------------------------------------------------------------------------
bool WriteTexture(std::ofstream& f, const std::string& baseName, vtkImageData* texture)
{
std::string mtlName = baseName + ".mtl";
std::ofstream fmtl(mtlName, std::ofstream::out);
if (fmtl.fail())
{
return false;
}
// write png file
std::string pngName = baseName + ".png";
vtkNew<vtkPNGWriter> pngWriter;
pngWriter->SetInputData(texture);
pngWriter->SetFileName(pngName.c_str());
pngWriter->Write();
// remove directories
mtlName = vtksys::SystemTools::GetFilenameName(mtlName);
pngName = vtksys::SystemTools::GetFilenameName(pngName);
// set material
fmtl << "newmtl vtktexture\n";
fmtl << "map_Kd " << pngName << "\n";
// declare material in obj file
f << "mtllib " + mtlName + "\n";
f << "usemtl vtktexture\n";
return true;
}
}
//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkOBJWriter);
//----------------------------------------------------------------------------
vtkOBJWriter::vtkOBJWriter()
{
this->FileName = nullptr;
this->SetNumberOfInputPorts(2);
}
//----------------------------------------------------------------------------
vtkOBJWriter::~vtkOBJWriter()
{
this->SetFileName(nullptr);
}
//----------------------------------------------------------------------------
void vtkOBJWriter::WriteData()
{
vtkPolyData* input = this->GetInputGeometry();
vtkImageData* texture = this->GetInputTexture();
if (input == nullptr)
{
vtkErrorMacro("No geometry to write!");
this->SetErrorCode(vtkErrorCode::UnknownError);
return;
}
vtkPoints* pts = input->GetPoints();
vtkCellArray* polys = input->GetPolys();
vtkCellArray* strips = input->GetStrips();
vtkCellArray* lines = input->GetLines();
vtkDataArray* normals = input->GetPointData()->GetNormals();
vtkDataArray* tcoords = input->GetPointData()->GetTCoords();
if (pts == nullptr)
{
vtkErrorMacro("No data to write!");
this->SetErrorCode(vtkErrorCode::UnknownError);
return;
}
if (this->FileName == nullptr)
{
vtkErrorMacro("Please specify FileName to write");
this->SetErrorCode(vtkErrorCode::NoFileNameError);
return;
}
vtkIdType npts = 0;
std::ofstream f(this->FileName, std::ofstream::out);
if (f.fail())
{
vtkErrorMacro("Unable to open file: " << this->FileName);
this->SetErrorCode(vtkErrorCode::CannotOpenFileError);
return;
}
// Write header
f << "# Generated by Visualization Toolkit\n";
// Write material if a texture is specified
if (texture)
{
std::vector<std::string> comp;
vtksys::SystemTools::SplitPath(vtksys::SystemTools::GetFilenamePath(this->FileName), comp);
comp.push_back(vtksys::SystemTools::GetFilenameWithoutLastExtension(this->FileName));
if (!::WriteTexture(f, vtksys::SystemTools::JoinPath(comp), texture))
{
vtkErrorMacro("Unable to create material file");
}
}
// Write points
::WritePoints(f, pts, normals, tcoords);
// Decompose any triangle strips into triangles
vtkNew<vtkCellArray> polyStrips;
if (strips->GetNumberOfCells() > 0)
{
vtkIdType* ptIds = nullptr;
for (strips->InitTraversal(); strips->GetNextCell(npts, ptIds);)
{
vtkTriangleStrip::DecomposeStrip(npts, ptIds, polyStrips);
}
}
// Write triangle strips
::WriteFaces(f, polyStrips, normals != nullptr, tcoords != nullptr);
// Write polygons.
if (polys)
{
::WriteFaces(f, polys, normals != nullptr, tcoords != nullptr);
}
// Write lines.
if (lines)
{
::WriteLines(f, lines);
}
f.close();
}
//----------------------------------------------------------------------------
void vtkOBJWriter::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "FileName: " << (this->GetFileName() ? this->GetFileName() : "(none)") << endl;
os << indent << "Input: " << this->GetInputGeometry() << endl;
vtkImageData* texture = this->GetInputTexture();
if (texture)
{
os << indent << "Texture:" << endl;
texture->PrintSelf(os, indent.GetNextIndent());
}
}
//----------------------------------------------------------------------------
vtkPolyData* vtkOBJWriter::GetInputGeometry()
{
return vtkPolyData::SafeDownCast(this->GetInput(0));
}
//----------------------------------------------------------------------------
vtkImageData* vtkOBJWriter::GetInputTexture()
{
return vtkImageData::SafeDownCast(this->GetInput(1));
}
//----------------------------------------------------------------------------
vtkDataSet* vtkOBJWriter::GetInput(int port)
{
return vtkDataSet::SafeDownCast(this->Superclass::GetInput(port));
}
//----------------------------------------------------------------------------
int vtkOBJWriter::FillInputPortInformation(int port, vtkInformation* info)
{
if (port == 0)
{
info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkPolyData");
return 1;
}
if (port == 1)
{
info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkImageData");
info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
return 1;
}
return 0;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkOBJWriter.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
/**
* @class vtkOBJWriter
* @brief write wavefront obj file
*
* vtkOBJWriter writes wavefront obj (.obj) files in ASCII form.
* OBJ files contain the geometry including lines, triangles and polygons.
* Normals and texture coordinates on points are also written if they exist.
* One can specify a texture passing a vtkImageData on port 1.
* If a texture is set, additionals .mtl and .png files are generated. Those files have the same
* name without obj extension.
*/
#ifndef vtkOBJWriter_h
#define vtkOBJWriter_h
#include "vtkIOGeometryModule.h" // For export macro
#include "vtkWriter.h"
class vtkDataSet;
class vtkImageData;
class vtkPolyData;
class VTKIOGEOMETRY_EXPORT vtkOBJWriter : public vtkWriter
{
public:
static vtkOBJWriter* New();
vtkTypeMacro(vtkOBJWriter, vtkWriter);
void PrintSelf(ostream& os, vtkIndent indent) override;
//@{
/**
* Get the inputs to this writer.
*/
vtkPolyData* GetInputGeometry();
vtkImageData* GetInputTexture();
vtkDataSet* GetInput(int port);
//@}
//@{
/**
* Get/Set the file name of the OBJ file.
*/
vtkSetStringMacro(FileName);
vtkGetStringMacro(FileName);
//@}
protected:
vtkOBJWriter();
~vtkOBJWriter() override;
void WriteData() override;
int FillInputPortInformation(int port, vtkInformation* info) override;
char* FileName;
private:
vtkOBJWriter(const vtkOBJWriter&) = delete;
void operator=(const vtkOBJWriter&) = delete;
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment