mirror of
https://github.com/scratchfoundation/bgfx.git
synced 2024-11-29 02:55:36 -05:00
238 lines
6.7 KiB
C++
238 lines
6.7 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Product: OpenCTM tools
|
|
// File: stl.cpp
|
|
// Description: Implementation of the STL 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 <stdexcept>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include "stl.h"
|
|
|
|
#ifdef _MSC_VER
|
|
typedef unsigned int uint32;
|
|
#else
|
|
#include <stdint.h>
|
|
typedef uint32_t uint32;
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
|
|
/// Read a 32-bit integer, endian independent.
|
|
static uint32 ReadInt32(istream &aStream)
|
|
{
|
|
unsigned char buf[4];
|
|
aStream.read((char *) buf, 4);
|
|
return ((uint32) buf[0]) | (((uint32) buf[1]) << 8) |
|
|
(((uint32) buf[2]) << 16) | (((uint32) buf[3]) << 24);
|
|
}
|
|
|
|
/// Write a 32-bit integer, endian independent.
|
|
static void WriteInt32(ostream &aStream, uint32 aValue)
|
|
{
|
|
unsigned char buf[4];
|
|
buf[0] = aValue & 255;
|
|
buf[1] = (aValue >> 8) & 255;
|
|
buf[2] = (aValue >> 16) & 255;
|
|
buf[3] = (aValue >> 24) & 255;
|
|
aStream.write((char *) buf, 4);
|
|
}
|
|
|
|
/// Read a Vector3, endian independent.
|
|
static Vector3 ReadVector3(istream &aStream)
|
|
{
|
|
union {
|
|
uint32 i;
|
|
float f;
|
|
} val;
|
|
Vector3 result;
|
|
val.i = ReadInt32(aStream);
|
|
result.x = val.f;
|
|
val.i = ReadInt32(aStream);
|
|
result.y = val.f;
|
|
val.i = ReadInt32(aStream);
|
|
result.z = val.f;
|
|
return result;
|
|
}
|
|
|
|
/// Write a Vector3, endian independent.
|
|
static void WriteVector3(ostream &aStream, Vector3 aValue)
|
|
{
|
|
union {
|
|
uint32 i;
|
|
float f;
|
|
} val;
|
|
val.f = aValue.x;
|
|
WriteInt32(aStream, val.i);
|
|
val.f = aValue.y;
|
|
WriteInt32(aStream, val.i);
|
|
val.f = aValue.z;
|
|
WriteInt32(aStream, val.i);
|
|
}
|
|
|
|
/// Vertex class used when reading and joining the triangle vertices.
|
|
class SortVertex {
|
|
public:
|
|
float x, y, z;
|
|
uint32 mOldIndex;
|
|
|
|
bool operator<(const SortVertex &v) const
|
|
{
|
|
return (x < v.x) || ((x == v.x) && ((y < v.y) || ((y == v.y) && (z < v.z))));
|
|
}
|
|
};
|
|
|
|
/// Import an STL file from a file.
|
|
void Import_STL(const char * aFileName, Mesh * aMesh)
|
|
{
|
|
// Clear the mesh
|
|
aMesh->Clear();
|
|
|
|
// Open the input file
|
|
ifstream f(aFileName, ios::in | ios::binary);
|
|
if(f.fail())
|
|
throw runtime_error("Could not open input file.");
|
|
|
|
// Get the file size
|
|
f.seekg(0, ios::end);
|
|
uint32 fileSize = (uint32) f.tellg();
|
|
f.seekg(0, ios::beg);
|
|
if(fileSize < 84)
|
|
throw runtime_error("Invalid format - not a valid STL file.");
|
|
|
|
// Read header (80 character comment + triangle count)
|
|
char comment[81];
|
|
f.read(comment, 80);
|
|
comment[80] = 0;
|
|
aMesh->mComment = string(comment);
|
|
uint32 triangleCount = ReadInt32(f);
|
|
if(fileSize != (84 + triangleCount * 50))
|
|
throw runtime_error("Invalid format - not a valid STL file.");
|
|
|
|
if(triangleCount > 0)
|
|
{
|
|
// Read all the triangle data
|
|
vector<SortVertex> vertices;
|
|
vertices.resize(triangleCount * 3);
|
|
for(uint32 i = 0; i < triangleCount; ++ i)
|
|
{
|
|
// Skip the flat normal
|
|
f.seekg(12, ios::cur);
|
|
|
|
// Read the three triangle vertices
|
|
for(uint32 j = 0; j < 3; ++ j)
|
|
{
|
|
Vector3 v = ReadVector3(f);
|
|
uint32 index = i * 3 + j;
|
|
vertices[index].x = v.x;
|
|
vertices[index].y = v.y;
|
|
vertices[index].z = v.z;
|
|
vertices[index].mOldIndex = index;
|
|
}
|
|
|
|
// Ignore the two fill bytes
|
|
f.seekg(2, ios::cur);
|
|
}
|
|
|
|
// Make sure that no redundant copies of vertices exist (STL files are full
|
|
// of vertex duplicates, so remove the redundancy), and store the data in
|
|
// the mesh object
|
|
sort(vertices.begin(), vertices.end());
|
|
aMesh->mVertices.resize(vertices.size());
|
|
aMesh->mIndices.resize(vertices.size());
|
|
SortVertex * firstEqual = &vertices[0];
|
|
int vertIdx = -1;
|
|
for(uint32 i = 0; i < vertices.size(); ++ i)
|
|
{
|
|
if((i == 0) ||
|
|
(vertices[i].z != firstEqual->z) ||
|
|
(vertices[i].y != firstEqual->y) ||
|
|
(vertices[i].x != firstEqual->x))
|
|
{
|
|
firstEqual = &vertices[i];
|
|
++ vertIdx;
|
|
aMesh->mVertices[vertIdx] = Vector3(firstEqual->x, firstEqual->y, firstEqual->z);
|
|
}
|
|
aMesh->mIndices[vertices[i].mOldIndex] = vertIdx;
|
|
}
|
|
aMesh->mVertices.resize(vertIdx + 1);
|
|
}
|
|
|
|
// Close the input file
|
|
f.close();
|
|
}
|
|
|
|
/// Export an STL file to a file.
|
|
void Export_STL(const char * aFileName, Mesh * aMesh, Options &aOptions)
|
|
{
|
|
// Open the output file
|
|
ofstream f(aFileName, ios::out | ios::binary);
|
|
if(f.fail())
|
|
throw runtime_error("Could not open output file.");
|
|
|
|
// Write header (80-character comment + triangle count)
|
|
char comment[80];
|
|
for(uint32 i = 0; i < 80; ++ i)
|
|
{
|
|
if(i < aMesh->mComment.size())
|
|
comment[i] = aMesh->mComment[i];
|
|
else
|
|
comment[i] = 0;
|
|
}
|
|
f.write(comment, 80);
|
|
uint32 triangleCount = aMesh->mIndices.size() / 3;
|
|
WriteInt32(f, triangleCount);
|
|
|
|
// Write the triangle data
|
|
for(uint32 i = 0; i < triangleCount; ++ i)
|
|
{
|
|
// Get the triangle vertices
|
|
Vector3 v1 = aMesh->mVertices[aMesh->mIndices[i * 3]];
|
|
Vector3 v2 = aMesh->mVertices[aMesh->mIndices[i * 3 + 1]];
|
|
Vector3 v3 = aMesh->mVertices[aMesh->mIndices[i * 3 + 2]];
|
|
|
|
// Calculate the triangle normal
|
|
Vector3 n1 = v2 - v1;
|
|
Vector3 n2 = v3 - v1;
|
|
Vector3 n = Normalize(Cross(n1, n2));
|
|
|
|
// Write the triangle normal
|
|
WriteVector3(f, n);
|
|
|
|
// Coordinates
|
|
WriteVector3(f, v1);
|
|
WriteVector3(f, v2);
|
|
WriteVector3(f, v3);
|
|
|
|
// Set the two fill bytes to zero
|
|
f.put(0);
|
|
f.put(0);
|
|
}
|
|
|
|
// Close the output file
|
|
f.close();
|
|
}
|