mirror of
https://github.com/scratchfoundation/bgfx.git
synced 2024-12-01 11:56:58 -05:00
321 lines
9.8 KiB
C++
321 lines
9.8 KiB
C++
|
//-----------------------------------------------------------------------------
|
||
|
// Product: OpenCTM tools
|
||
|
// File: ply.cpp
|
||
|
// Description: Implementation of the PLY file format importer/exporter.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Copyright (c) 2009-2010 Marcus Geelnard
|
||
|
//
|
||
|
// This software is provided 'as-is', without any express or implied
|
||
|
// warranty. In no event will the authors be held liable for any damages
|
||
|
// arising from the use of this software.
|
||
|
//
|
||
|
// Permission is granted to anyone to use this software for any purpose,
|
||
|
// including commercial applications, and to alter it and redistribute it
|
||
|
// freely, subject to the following restrictions:
|
||
|
//
|
||
|
// 1. The origin of this software must not be misrepresented; you must not
|
||
|
// claim that you wrote the original software. If you use this software
|
||
|
// in a product, an acknowledgment in the product documentation would be
|
||
|
// appreciated but is not required.
|
||
|
//
|
||
|
// 2. Altered source versions must be plainly marked as such, and must not
|
||
|
// be misrepresented as being the original software.
|
||
|
//
|
||
|
// 3. This notice may not be removed or altered from any source
|
||
|
// distribution.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <stdexcept>
|
||
|
#include <string>
|
||
|
#include <fstream>
|
||
|
#include <iomanip>
|
||
|
#include <sstream>
|
||
|
#include <vector>
|
||
|
#include <clocale>
|
||
|
#include <rply.h>
|
||
|
#include "ply.h"
|
||
|
#include "common.h"
|
||
|
|
||
|
using namespace std;
|
||
|
|
||
|
typedef struct {
|
||
|
Mesh * mMesh;
|
||
|
long mFaceIdx;
|
||
|
long mVertexIdx;
|
||
|
long mNormalIdx;
|
||
|
long mTexCoordIdx;
|
||
|
long mColorIdx;
|
||
|
} PLYReaderState;
|
||
|
|
||
|
|
||
|
static int PLYFaceCallback(p_ply_argument argument)
|
||
|
{
|
||
|
PLYReaderState * state;
|
||
|
long dummy, length, valueIndex;
|
||
|
ply_get_argument_user_data(argument, (void **) &state, &dummy);
|
||
|
double value = ply_get_argument_value(argument);
|
||
|
ply_get_argument_property(argument, NULL, &length, &valueIndex);
|
||
|
if((valueIndex >= 0) && (valueIndex <= 2))
|
||
|
state->mMesh->mIndices[state->mFaceIdx * 3 + valueIndex] = int(value);
|
||
|
if(valueIndex == 2)
|
||
|
++ state->mFaceIdx;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int PLYVertexCallback(p_ply_argument argument)
|
||
|
{
|
||
|
PLYReaderState * state;
|
||
|
long index;
|
||
|
ply_get_argument_user_data(argument, (void **) &state, &index);
|
||
|
double value = ply_get_argument_value(argument);
|
||
|
switch(index)
|
||
|
{
|
||
|
case 0:
|
||
|
state->mMesh->mVertices[state->mVertexIdx].x = float(value);
|
||
|
break;
|
||
|
case 1:
|
||
|
state->mMesh->mVertices[state->mVertexIdx].y = float(value);
|
||
|
break;
|
||
|
case 2:
|
||
|
state->mMesh->mVertices[state->mVertexIdx].z = float(value);
|
||
|
++ state->mVertexIdx;
|
||
|
break;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int PLYNormalCallback(p_ply_argument argument)
|
||
|
{
|
||
|
PLYReaderState * state;
|
||
|
long index;
|
||
|
ply_get_argument_user_data(argument, (void **) &state, &index);
|
||
|
double value = ply_get_argument_value(argument);
|
||
|
switch(index)
|
||
|
{
|
||
|
case 0:
|
||
|
state->mMesh->mNormals[state->mNormalIdx].x = float(value);
|
||
|
break;
|
||
|
case 1:
|
||
|
state->mMesh->mNormals[state->mNormalIdx].y = float(value);
|
||
|
break;
|
||
|
case 2:
|
||
|
state->mMesh->mNormals[state->mNormalIdx].z = float(value);
|
||
|
++ state->mNormalIdx;
|
||
|
break;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int PLYTexCoordCallback(p_ply_argument argument)
|
||
|
{
|
||
|
PLYReaderState * state;
|
||
|
long index;
|
||
|
ply_get_argument_user_data(argument, (void **) &state, &index);
|
||
|
double value = ply_get_argument_value(argument);
|
||
|
switch(index)
|
||
|
{
|
||
|
case 0:
|
||
|
state->mMesh->mTexCoords[state->mTexCoordIdx].u = float(value);
|
||
|
break;
|
||
|
case 1:
|
||
|
state->mMesh->mTexCoords[state->mTexCoordIdx].v = float(value);
|
||
|
++ state->mTexCoordIdx;
|
||
|
break;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int PLYColorCallback(p_ply_argument argument)
|
||
|
{
|
||
|
PLYReaderState * state;
|
||
|
long index;
|
||
|
ply_get_argument_user_data(argument, (void **) &state, &index);
|
||
|
double value = ply_get_argument_value(argument);
|
||
|
switch(index)
|
||
|
{
|
||
|
case 0:
|
||
|
state->mMesh->mColors[state->mColorIdx].x = float(value) / 255.0f;
|
||
|
break;
|
||
|
case 1:
|
||
|
state->mMesh->mColors[state->mColorIdx].y = float(value) / 255.0f;
|
||
|
break;
|
||
|
case 2:
|
||
|
state->mMesh->mColors[state->mColorIdx].z = float(value) / 255.0f;
|
||
|
++ state->mColorIdx;
|
||
|
break;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/// Import a PLY file from a file.
|
||
|
void Import_PLY(const char * aFileName, Mesh * aMesh)
|
||
|
{
|
||
|
// Start by ensuring that we use proper locale settings for the file format
|
||
|
setlocale(LC_NUMERIC, "C");
|
||
|
|
||
|
// Clear the mesh
|
||
|
aMesh->Clear();
|
||
|
|
||
|
// Initialize the state
|
||
|
PLYReaderState state;
|
||
|
state.mMesh = aMesh;
|
||
|
state.mFaceIdx = 0;
|
||
|
state.mVertexIdx = 0;
|
||
|
state.mNormalIdx = 0;
|
||
|
state.mTexCoordIdx = 0;
|
||
|
state.mColorIdx = 0;
|
||
|
|
||
|
// Open the PLY file
|
||
|
p_ply ply = ply_open(aFileName, NULL);
|
||
|
if(!ply)
|
||
|
throw runtime_error("Unable to open PLY file.");
|
||
|
if(!ply_read_header(ply))
|
||
|
throw runtime_error("Invalid PLY file.");
|
||
|
|
||
|
// Get the file comment (if any)
|
||
|
bool firstComment = true;
|
||
|
const char * comment = ply_get_next_comment(ply, NULL);
|
||
|
while(comment)
|
||
|
{
|
||
|
if(firstComment)
|
||
|
aMesh->mComment = string(comment);
|
||
|
else
|
||
|
aMesh->mComment += string(" ") + string(comment);
|
||
|
firstComment = false;
|
||
|
comment = ply_get_next_comment(ply, comment);
|
||
|
}
|
||
|
|
||
|
// Set face callback
|
||
|
long faceCount = ply_set_read_cb(ply, "face", "vertex_indices", PLYFaceCallback, &state, 0);
|
||
|
if(faceCount == 0)
|
||
|
faceCount = ply_set_read_cb(ply, "face", "vertex_index", PLYFaceCallback, &state, 0);
|
||
|
|
||
|
// Set vertex callback
|
||
|
long vertexCount = ply_set_read_cb(ply, "vertex", "x", PLYVertexCallback, &state, 0);
|
||
|
ply_set_read_cb(ply, "vertex", "y", PLYVertexCallback, &state, 1);
|
||
|
ply_set_read_cb(ply, "vertex", "z", PLYVertexCallback, &state, 2);
|
||
|
|
||
|
// Set normal callback
|
||
|
long normalCount = ply_set_read_cb(ply, "vertex", "nx", PLYNormalCallback, &state, 0);
|
||
|
ply_set_read_cb(ply, "vertex", "ny", PLYNormalCallback, &state, 1);
|
||
|
ply_set_read_cb(ply, "vertex", "nz", PLYNormalCallback, &state, 2);
|
||
|
|
||
|
// Set tex coord callback
|
||
|
long texCoordCount = ply_set_read_cb(ply, "vertex", "s", PLYTexCoordCallback, &state, 0);
|
||
|
ply_set_read_cb(ply, "vertex", "t", PLYTexCoordCallback, &state, 1);
|
||
|
|
||
|
// Set color callback
|
||
|
long colorCount = ply_set_read_cb(ply, "vertex", "red", PLYColorCallback, &state, 0);
|
||
|
ply_set_read_cb(ply, "vertex", "green", PLYColorCallback, &state, 1);
|
||
|
ply_set_read_cb(ply, "vertex", "blue", PLYColorCallback, &state, 2);
|
||
|
|
||
|
// Sanity check
|
||
|
if((faceCount < 1) || (vertexCount < 1))
|
||
|
throw runtime_error("Empty PLY mesh - invalid file format?");
|
||
|
|
||
|
// Prepare the mesh
|
||
|
aMesh->mIndices.resize(faceCount * 3);
|
||
|
aMesh->mVertices.resize(vertexCount);
|
||
|
aMesh->mNormals.resize(normalCount);
|
||
|
aMesh->mTexCoords.resize(texCoordCount);
|
||
|
aMesh->mColors.resize(colorCount);
|
||
|
|
||
|
// Read the PLY file
|
||
|
if(!ply_read(ply))
|
||
|
throw runtime_error("Unable to load PLY file.");
|
||
|
|
||
|
// Close the PLY file
|
||
|
ply_close(ply);
|
||
|
}
|
||
|
|
||
|
/// Export a PLY file to a file.
|
||
|
void Export_PLY(const char * aFileName, Mesh * aMesh, Options &aOptions)
|
||
|
{
|
||
|
// Start by ensuring that we use proper locale settings for the file format
|
||
|
setlocale(LC_NUMERIC, "C");
|
||
|
|
||
|
// What should we export?
|
||
|
bool exportTexCoords = aMesh->HasTexCoords() && !aOptions.mNoTexCoords;
|
||
|
bool exportNormals = aMesh->HasNormals() && !aOptions.mNoNormals;
|
||
|
bool exportColors = aMesh->HasColors() && !aOptions.mNoColors;
|
||
|
|
||
|
// Open the output file
|
||
|
ofstream f(aFileName, ios::out | ios::binary);
|
||
|
if(f.fail())
|
||
|
throw runtime_error("Could not open output file.");
|
||
|
|
||
|
// Set floating point precision
|
||
|
f << setprecision(8);
|
||
|
|
||
|
// Write header
|
||
|
f << "ply" << endl;
|
||
|
f << "format ascii 1.0" << endl;
|
||
|
if(aMesh->mComment.size() > 0)
|
||
|
{
|
||
|
stringstream sstr(aMesh->mComment);
|
||
|
sstr.seekg(0);
|
||
|
while(!sstr.eof())
|
||
|
{
|
||
|
string line;
|
||
|
getline(sstr, line);
|
||
|
line = TrimString(line);
|
||
|
if(line.size() > 0)
|
||
|
f << "comment " << line << endl;
|
||
|
}
|
||
|
}
|
||
|
f << "element vertex " << aMesh->mVertices.size() << endl;
|
||
|
f << "property float x" << endl;
|
||
|
f << "property float y" << endl;
|
||
|
f << "property float z" << endl;
|
||
|
if(exportTexCoords)
|
||
|
{
|
||
|
f << "property float s" << endl;
|
||
|
f << "property float t" << endl;
|
||
|
}
|
||
|
if(exportNormals)
|
||
|
{
|
||
|
f << "property float nx" << endl;
|
||
|
f << "property float ny" << endl;
|
||
|
f << "property float nz" << endl;
|
||
|
}
|
||
|
if(exportColors)
|
||
|
{
|
||
|
f << "property uchar red" << endl;
|
||
|
f << "property uchar green" << endl;
|
||
|
f << "property uchar blue" << endl;
|
||
|
}
|
||
|
f << "element face " << aMesh->mIndices.size() / 3 << endl;
|
||
|
f << "property list uchar int vertex_indices" << endl;
|
||
|
f << "end_header" << endl;
|
||
|
|
||
|
// Write vertices
|
||
|
for(unsigned int i = 0; i < aMesh->mVertices.size(); ++ i)
|
||
|
{
|
||
|
f << aMesh->mVertices[i].x << " " <<
|
||
|
aMesh->mVertices[i].y << " " <<
|
||
|
aMesh->mVertices[i].z;
|
||
|
if(exportTexCoords)
|
||
|
f << " " << aMesh->mTexCoords[i].u << " " <<
|
||
|
aMesh->mTexCoords[i].v;
|
||
|
if(exportNormals)
|
||
|
f << " " << aMesh->mNormals[i].x << " " <<
|
||
|
aMesh->mNormals[i].y << " " <<
|
||
|
aMesh->mNormals[i].z;
|
||
|
if(exportColors)
|
||
|
f << " " << int(floorf(255.0f * aMesh->mColors[i].x + 0.5f)) << " " <<
|
||
|
int(floorf(255.0f * aMesh->mColors[i].y + 0.5f)) << " " <<
|
||
|
int(floorf(255.0f * aMesh->mColors[i].z + 0.5f));
|
||
|
f << endl;
|
||
|
}
|
||
|
|
||
|
// Write faces
|
||
|
for(unsigned int i = 0; i < aMesh->mIndices.size() / 3; ++ i)
|
||
|
f << "3 " << aMesh->mIndices[i * 3] << " " <<
|
||
|
aMesh->mIndices[i * 3 + 1] << " " <<
|
||
|
aMesh->mIndices[i * 3 + 2] << endl;
|
||
|
|
||
|
// Close the output file
|
||
|
f.close();
|
||
|
}
|