2010-09-03 05:41:20 +08:00
|
|
|
/*
|
|
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
|
|
|
|
Copyright (c) 2007-2010 by Wenzel Jakob and others.
|
|
|
|
|
|
|
|
Mitsuba is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License Version 3
|
|
|
|
as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
Mitsuba is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
#include <mitsuba/render/trimesh.h>
|
|
|
|
#include <mitsuba/core/random.h>
|
|
|
|
#include <mitsuba/core/plugin.h>
|
2010-08-31 03:40:32 +08:00
|
|
|
#include <mitsuba/core/zstream.h>
|
2010-09-10 09:14:48 +08:00
|
|
|
#include <mitsuba/core/properties.h>
|
|
|
|
#include <mitsuba/render/subsurface.h>
|
|
|
|
#include <mitsuba/render/bsdf.h>
|
|
|
|
#include <mitsuba/render/luminaire.h>
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2010-08-31 03:40:32 +08:00
|
|
|
#define MTS_FILEFORMAT_HEADER 0x041C
|
2010-10-16 06:33:32 +08:00
|
|
|
#define MTS_FILEFORMAT_VERSION_V3 0x03
|
2010-08-10 01:38:37 +08:00
|
|
|
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
|
2010-10-17 06:08:55 +08:00
|
|
|
TriMesh::TriMesh(const std::string &name, size_t triangleCount,
|
|
|
|
size_t vertexCount, bool hasNormals, bool hasTexcoords,
|
|
|
|
bool hasVertexColors, bool flipNormals, bool faceNormals)
|
2010-10-16 06:33:32 +08:00
|
|
|
: Shape(Properties()), m_triangleCount(triangleCount),
|
|
|
|
m_vertexCount(vertexCount), m_flipNormals(flipNormals),
|
|
|
|
m_faceNormals(faceNormals) {
|
2010-08-18 23:28:27 +08:00
|
|
|
m_name = name;
|
2010-10-16 06:33:32 +08:00
|
|
|
|
|
|
|
m_triangles = new Triangle[m_triangleCount];
|
|
|
|
m_positions = new Point[m_vertexCount];
|
|
|
|
m_normals = hasNormals ? new Normal[m_vertexCount] : NULL;
|
|
|
|
m_texcoords = hasTexcoords ? new Point2[m_vertexCount] : NULL;
|
|
|
|
m_colors = hasVertexColors ? new Spectrum[m_vertexCount] : NULL;
|
|
|
|
m_tangents = NULL;
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TriMesh::TriMesh(const Properties &props)
|
2010-10-16 06:33:32 +08:00
|
|
|
: Shape(props), m_triangles(NULL), m_positions(NULL),
|
|
|
|
m_normals(NULL), m_texcoords(NULL), m_tangents(NULL),
|
|
|
|
m_colors(NULL) {
|
|
|
|
|
|
|
|
/* By default, any existing normals will be used for
|
|
|
|
rendering. If no normals are found, Mitsuba will
|
|
|
|
automatically generate smooth vertex normals.
|
|
|
|
Setting the 'faceNormals' parameter instead forces
|
|
|
|
the use of face normals, which will result in a faceted
|
|
|
|
appearance.
|
|
|
|
*/
|
|
|
|
m_faceNormals = props.getBoolean("faceNormals", false);
|
|
|
|
|
|
|
|
/* Causes all normals to be flipped */
|
2010-08-10 01:38:37 +08:00
|
|
|
m_flipNormals = props.getBoolean("flipNormals", false);
|
2010-10-16 06:33:32 +08:00
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
m_triangles = NULL;
|
|
|
|
}
|
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
/* Flags used to identify available data during serialization */
|
|
|
|
enum ETriMeshFlags {
|
|
|
|
EHasNormals = 0x0001,
|
|
|
|
EHasTexcoords = 0x0002,
|
|
|
|
EHasTangents = 0x0004,
|
|
|
|
EHasColors = 0x0008,
|
|
|
|
EFaceNormals = 0x0010,
|
|
|
|
ESinglePrecision = 0x1000,
|
|
|
|
EDoublePrecision = 0x2000
|
|
|
|
};
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
TriMesh::TriMesh(Stream *stream, InstanceManager *manager)
|
2010-10-16 06:33:32 +08:00
|
|
|
: Shape(stream, manager), m_tangents(NULL) {
|
2010-10-17 06:08:55 +08:00
|
|
|
m_name = stream->readString();
|
|
|
|
m_aabb = AABB(stream);
|
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
uint32_t flags = stream->readUInt();
|
2010-08-10 01:38:37 +08:00
|
|
|
m_vertexCount = (size_t) stream->readULong();
|
|
|
|
m_triangleCount = (size_t) stream->readULong();
|
2010-10-16 06:33:32 +08:00
|
|
|
|
|
|
|
m_positions = new Point[m_vertexCount];
|
|
|
|
stream->readFloatArray(reinterpret_cast<Float *>(m_positions),
|
|
|
|
m_vertexCount * sizeof(Point)/sizeof(Float));
|
|
|
|
|
|
|
|
m_faceNormals = flags & EFaceNormals;
|
|
|
|
|
|
|
|
if (flags & EHasNormals) {
|
|
|
|
m_normals = new Normal[m_vertexCount];
|
|
|
|
stream->readFloatArray(reinterpret_cast<Float *>(m_normals),
|
|
|
|
m_vertexCount * sizeof(Normal)/sizeof(Float));
|
|
|
|
} else {
|
|
|
|
m_normals = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & EHasTexcoords) {
|
|
|
|
m_texcoords = new Point2[m_vertexCount];
|
|
|
|
stream->readFloatArray(reinterpret_cast<Float *>(m_texcoords),
|
|
|
|
m_vertexCount * sizeof(Point2)/sizeof(Float));
|
|
|
|
} else {
|
|
|
|
m_texcoords = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & EHasColors) {
|
|
|
|
m_colors = new Spectrum[m_vertexCount];
|
|
|
|
stream->readFloatArray(reinterpret_cast<Float *>(m_colors),
|
|
|
|
m_vertexCount * sizeof(Spectrum)/sizeof(Float));
|
|
|
|
} else {
|
|
|
|
m_colors = NULL;
|
|
|
|
}
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
m_triangles = new Triangle[m_triangleCount];
|
2010-10-16 06:33:32 +08:00
|
|
|
stream->readUIntArray(reinterpret_cast<uint32_t *>(m_triangles),
|
|
|
|
m_triangleCount * sizeof(Triangle)/sizeof(uint32_t));
|
2010-08-10 01:38:37 +08:00
|
|
|
m_flipNormals = false;
|
|
|
|
configure();
|
|
|
|
}
|
2010-10-16 06:33:32 +08:00
|
|
|
|
|
|
|
static void readHelper(Stream *stream, bool fileDoublePrecision,
|
|
|
|
Float *target, size_t count, size_t nelems) {
|
|
|
|
#if defined(SINGLE_PRECISION)
|
|
|
|
bool hostDoublePrecision = false;
|
|
|
|
#else
|
|
|
|
bool hostDoublePrecision = true;
|
|
|
|
#endif
|
|
|
|
size_t size = count * nelems;
|
|
|
|
if (fileDoublePrecision == hostDoublePrecision) {
|
|
|
|
/* Precision matches - load directly into memory */
|
|
|
|
stream->readFloatArray(target, size);
|
|
|
|
} else if (fileDoublePrecision) {
|
|
|
|
/* Double -> Single conversion */
|
|
|
|
double *temp = new double[size];
|
|
|
|
stream->readDoubleArray(temp, size);
|
|
|
|
for (size_t i=0; i<size; ++i)
|
|
|
|
target[i] = (Float) temp[i];
|
|
|
|
delete[] temp;
|
|
|
|
} else {
|
|
|
|
/* Single -> Double conversion */
|
|
|
|
float *temp = new float[size];
|
|
|
|
stream->readSingleArray(temp, size);
|
|
|
|
for (size_t i=0; i<size; ++i)
|
|
|
|
target[i] = (Float) temp[i];
|
|
|
|
delete[] temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-16 03:51:31 +08:00
|
|
|
TriMesh::TriMesh(Stream *_stream, int index)
|
|
|
|
: Shape(Properties()), m_tangents(NULL) {
|
2010-08-31 03:40:32 +08:00
|
|
|
ref<Stream> stream = _stream;
|
|
|
|
|
2010-11-16 03:51:31 +08:00
|
|
|
if (index != 0) {
|
|
|
|
/* Determine the position of the requested substream. This
|
|
|
|
is stored at the end of the file */
|
|
|
|
stream->setPos(stream->getSize() - sizeof(uint32_t));
|
|
|
|
uint32_t count = stream->readUInt();
|
|
|
|
if (index < 0 || index > (int) count) {
|
|
|
|
Log(EError, "Unable to unserialize mesh, "
|
|
|
|
"shape index is out of range! (requested %i out of 0..%i)",
|
|
|
|
index, count-1);
|
|
|
|
}
|
|
|
|
stream->setPos(stream->getSize() - sizeof(uint32_t) * (1+count-index));
|
|
|
|
// Seek to the correct position
|
|
|
|
stream->setPos(stream->readUInt());
|
|
|
|
}
|
|
|
|
|
2010-10-16 08:03:38 +08:00
|
|
|
if (stream->getByteOrder() != Stream::ELittleEndian)
|
2010-08-10 01:38:37 +08:00
|
|
|
Log(EError, "Tried to unserialize a shape from a stream, "
|
2010-10-16 08:03:38 +08:00
|
|
|
"which was not previously set to little endian byte order!");
|
2010-10-16 06:33:32 +08:00
|
|
|
|
2010-10-26 05:44:38 +08:00
|
|
|
short format = stream->readShort();
|
|
|
|
if (format == 0x1C04)
|
2010-10-26 05:41:16 +08:00
|
|
|
Log(EError, "Encountered a geometry file generated by an old "
|
|
|
|
"version of Mitsuba. Please re-import the scene to update this file "
|
|
|
|
"to the current format.");
|
|
|
|
|
2010-10-26 05:44:38 +08:00
|
|
|
if (format != MTS_FILEFORMAT_HEADER)
|
2010-08-10 01:38:37 +08:00
|
|
|
Log(EError, "Encountered an invalid file format!");
|
|
|
|
|
2010-08-31 03:40:32 +08:00
|
|
|
short version = stream->readShort();
|
2010-10-16 06:33:32 +08:00
|
|
|
if (version != MTS_FILEFORMAT_VERSION_V3)
|
2010-08-10 01:38:37 +08:00
|
|
|
Log(EError, "Encountered an incompatible file version!");
|
2010-10-16 06:33:32 +08:00
|
|
|
stream = new ZStream(stream);
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
uint32_t flags = stream->readUInt();
|
2010-08-10 01:38:37 +08:00
|
|
|
m_vertexCount = (size_t) stream->readULong();
|
2010-10-16 06:33:32 +08:00
|
|
|
m_triangleCount = (size_t) stream->readULong();
|
|
|
|
|
|
|
|
bool fileDoublePrecision = flags & EDoublePrecision;
|
|
|
|
m_faceNormals = flags & EFaceNormals;
|
|
|
|
|
|
|
|
m_positions = new Point[m_vertexCount];
|
|
|
|
readHelper(stream, fileDoublePrecision,
|
|
|
|
reinterpret_cast<Float *>(m_positions),
|
|
|
|
m_vertexCount, sizeof(Point)/sizeof(Float));
|
|
|
|
|
|
|
|
if (flags & EHasNormals) {
|
|
|
|
m_normals = new Normal[m_vertexCount];
|
|
|
|
readHelper(stream, fileDoublePrecision,
|
|
|
|
reinterpret_cast<Float *>(m_normals),
|
|
|
|
m_vertexCount, sizeof(Normal)/sizeof(Float));
|
2010-10-16 08:03:38 +08:00
|
|
|
} else {
|
|
|
|
m_normals = NULL;
|
2010-10-16 06:33:32 +08:00
|
|
|
}
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
if (flags & EHasTexcoords) {
|
|
|
|
m_texcoords = new Point2[m_vertexCount];
|
|
|
|
readHelper(stream, fileDoublePrecision,
|
|
|
|
reinterpret_cast<Float *>(m_texcoords),
|
|
|
|
m_vertexCount, sizeof(Point2)/sizeof(Float));
|
2010-10-16 08:03:38 +08:00
|
|
|
} else {
|
|
|
|
m_texcoords = NULL;
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
if (flags & EHasColors) {
|
|
|
|
m_colors = new Spectrum[m_vertexCount];
|
|
|
|
readHelper(stream, fileDoublePrecision,
|
|
|
|
reinterpret_cast<Float *>(m_colors),
|
|
|
|
m_vertexCount, sizeof(Spectrum)/sizeof(Float));
|
2010-10-16 08:03:38 +08:00
|
|
|
} else {
|
|
|
|
m_colors = NULL;
|
2010-10-16 06:33:32 +08:00
|
|
|
}
|
|
|
|
|
2010-10-16 08:03:38 +08:00
|
|
|
m_triangles = new Triangle[m_triangleCount];
|
2010-10-16 06:33:32 +08:00
|
|
|
stream->readUIntArray(reinterpret_cast<uint32_t *>(m_triangles),
|
|
|
|
m_triangleCount * sizeof(Triangle)/sizeof(uint32_t));
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
m_flipNormals = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TriMesh::~TriMesh() {
|
2010-10-16 06:33:32 +08:00
|
|
|
if (m_positions)
|
|
|
|
delete[] m_positions;
|
|
|
|
if (m_normals)
|
|
|
|
delete[] m_normals;
|
|
|
|
if (m_texcoords)
|
|
|
|
delete[] m_texcoords;
|
|
|
|
if (m_tangents)
|
|
|
|
delete[] m_tangents;
|
|
|
|
if (m_colors)
|
|
|
|
delete[] m_colors;
|
2010-08-10 01:38:37 +08:00
|
|
|
if (m_triangles)
|
|
|
|
delete[] m_triangles;
|
|
|
|
}
|
2010-10-17 06:08:55 +08:00
|
|
|
|
|
|
|
std::string TriMesh::getName() const {
|
|
|
|
return m_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
AABB TriMesh::getAABB() const {
|
|
|
|
return m_aabb;
|
|
|
|
}
|
|
|
|
|
|
|
|
Float TriMesh::pdfArea(const ShapeSamplingRecord &sRec) const {
|
|
|
|
return m_invSurfaceArea;
|
|
|
|
}
|
2010-08-10 01:38:37 +08:00
|
|
|
|
|
|
|
void TriMesh::configure() {
|
|
|
|
Shape::configure();
|
|
|
|
|
|
|
|
if (m_areaPDF.isReady())
|
|
|
|
return;
|
2010-10-16 06:33:32 +08:00
|
|
|
|
|
|
|
m_aabb.reset();
|
2010-09-01 05:38:23 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
if (m_triangleCount == 0)
|
|
|
|
Log(EError, "Encountered an empty triangle mesh!");
|
2010-09-29 01:47:16 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
/* Generate a PDF for sampling wrt. area */
|
2010-09-29 01:47:16 +08:00
|
|
|
for (size_t i=0; i<m_triangleCount; i++)
|
2010-10-16 06:33:32 +08:00
|
|
|
m_areaPDF.put(m_triangles[i].surfaceArea(m_positions));
|
2010-08-10 01:38:37 +08:00
|
|
|
m_surfaceArea = m_areaPDF.build();
|
|
|
|
m_invSurfaceArea = 1.0f / m_surfaceArea;
|
2010-10-16 06:33:32 +08:00
|
|
|
|
|
|
|
/* Determine the object bounds */
|
2010-08-10 01:38:37 +08:00
|
|
|
for (size_t i=0; i<m_vertexCount; i++)
|
2010-10-16 06:33:32 +08:00
|
|
|
m_aabb.expandBy(m_positions[i]);
|
|
|
|
computeNormals();
|
2010-10-16 18:29:58 +08:00
|
|
|
|
2010-10-16 10:01:21 +08:00
|
|
|
if ((m_bsdf->getType() & BSDF::EAnisotropicMaterial
|
|
|
|
|| m_bsdf->usesRayDifferentials()) && !m_tangents)
|
2010-10-16 06:33:32 +08:00
|
|
|
computeTangentSpaceBasis();
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2010-10-17 06:08:55 +08:00
|
|
|
Float TriMesh::getSurfaceArea() const {
|
|
|
|
return m_surfaceArea;
|
|
|
|
}
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
Float TriMesh::sampleArea(ShapeSamplingRecord &sRec, const Point2 &sample) const {
|
|
|
|
Point2 newSeed = sample;
|
|
|
|
int index = m_areaPDF.sampleReuse(newSeed.y);
|
2010-10-16 06:33:32 +08:00
|
|
|
sRec.p = m_triangles[index].sample(m_positions, m_normals, sRec.n, newSeed);
|
2010-08-10 01:38:37 +08:00
|
|
|
return m_invSurfaceArea;
|
|
|
|
}
|
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
void TriMesh::computeNormals() {
|
|
|
|
int zeroArea = 0, invalidNormals = 0;
|
|
|
|
if (m_faceNormals) {
|
|
|
|
if (m_normals) {
|
|
|
|
delete[] m_normals;
|
|
|
|
m_normals = NULL;
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
if (m_flipNormals) {
|
|
|
|
/* Change the winding order */
|
|
|
|
for (size_t i=0; i<m_triangleCount; ++i) {
|
|
|
|
Triangle &t = m_triangles[i];
|
|
|
|
std::swap(t.idx[0], t.idx[1]);
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2010-10-16 06:33:32 +08:00
|
|
|
if (m_normals) {
|
|
|
|
if (m_flipNormals) {
|
|
|
|
for (size_t i=0; i<m_vertexCount; i++)
|
|
|
|
m_normals[i] *= -1;
|
|
|
|
} else {
|
|
|
|
/* Do nothing */
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
2010-10-16 06:33:32 +08:00
|
|
|
} else {
|
|
|
|
m_normals = new Normal[m_vertexCount];
|
|
|
|
memset(m_normals, 0, sizeof(Normal)*m_vertexCount);
|
|
|
|
|
|
|
|
for (size_t i=0; i<m_triangleCount; i++) {
|
|
|
|
const Triangle &tri = m_triangles[i];
|
|
|
|
const Point &v0 = m_positions[tri.idx[0]];
|
|
|
|
const Point &v1 = m_positions[tri.idx[1]];
|
|
|
|
const Point &v2 = m_positions[tri.idx[2]];
|
2010-08-10 01:38:37 +08:00
|
|
|
Normal n = Normal(cross(v1 - v0, v2 - v0));
|
|
|
|
Float length = n.length();
|
2010-09-01 04:32:34 +08:00
|
|
|
if (length != 0) {
|
2010-08-10 01:38:37 +08:00
|
|
|
n /= length;
|
2010-10-16 06:33:32 +08:00
|
|
|
m_normals[tri.idx[0]] += n;
|
|
|
|
m_normals[tri.idx[1]] += n;
|
|
|
|
m_normals[tri.idx[2]] += n;
|
2010-09-01 04:32:34 +08:00
|
|
|
} else {
|
2010-08-31 06:23:34 +08:00
|
|
|
zeroArea++;
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
for (size_t i=0; i<m_vertexCount; i++) {
|
|
|
|
Normal &n = m_normals[i];
|
2010-08-10 01:38:37 +08:00
|
|
|
Float length = n.length();
|
2010-10-16 06:33:32 +08:00
|
|
|
if (m_flipNormals)
|
|
|
|
length *= -1;
|
2010-09-01 04:32:34 +08:00
|
|
|
if (length != 0) {
|
2010-08-10 01:38:37 +08:00
|
|
|
n /= length;
|
2010-09-01 04:32:34 +08:00
|
|
|
} else {
|
2010-10-16 06:33:32 +08:00
|
|
|
/* Choose some bogus value */
|
|
|
|
invalidNormals++;
|
|
|
|
n = Normal(1, 0, 0);
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
}
|
2010-10-16 06:33:32 +08:00
|
|
|
}
|
|
|
|
}
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
m_flipNormals = false;
|
|
|
|
|
|
|
|
if (invalidNormals > 0)
|
|
|
|
Log(EWarn, "\"%s\": Unable to generate %i vertex normals",
|
|
|
|
m_name.c_str(), invalidNormals);
|
|
|
|
|
|
|
|
if (zeroArea > 0)
|
|
|
|
Log(EWarn, "\"%s\": Mesh contains %i zero area triangles",
|
|
|
|
m_name.c_str(), zeroArea);
|
|
|
|
}
|
|
|
|
|
2010-10-25 23:18:10 +08:00
|
|
|
bool TriMesh::computeTangentSpaceBasis() {
|
2010-10-16 06:33:32 +08:00
|
|
|
int zeroArea = 0, zeroNormals = 0;
|
2010-10-25 23:18:10 +08:00
|
|
|
if (!m_texcoords) {
|
|
|
|
if (m_bsdf->getType() & BSDF::EAnisotropicMaterial)
|
|
|
|
Log(EError, "\"%s\": computeTangentSpace(): texture coordinates are required "
|
|
|
|
"to generate tangent vectors. If you want to render with an anisotropic "
|
|
|
|
"material, make sure that all assigned objects have texture coordinates.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
if (m_tangents)
|
|
|
|
Log(EError, "Tangent space vectors have already been generated!");
|
|
|
|
|
|
|
|
m_tangents = new TangentSpace[m_vertexCount];
|
|
|
|
memset(m_tangents, 0, sizeof(TangentSpace));
|
|
|
|
|
|
|
|
/* No. of triangles sharing a vertex */
|
|
|
|
uint32_t *sharers = new uint32_t[m_vertexCount];
|
|
|
|
|
|
|
|
for (size_t i=0; i<m_vertexCount; i++) {
|
|
|
|
m_tangents[i].dpdu = Vector(0.0f);
|
|
|
|
m_tangents[i].dpdv = Vector(0.0f);
|
|
|
|
if (m_normals[i].isZero()) {
|
|
|
|
zeroNormals++;
|
|
|
|
m_normals[i] = Normal(1.0f, 0.0f, 0.0f);
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
2010-10-16 06:33:32 +08:00
|
|
|
sharers[i] = 0;
|
|
|
|
}
|
2010-10-16 18:29:58 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
for (size_t i=0; i<m_triangleCount; i++) {
|
|
|
|
uint32_t idx0 = m_triangles[i].idx[0],
|
|
|
|
idx1 = m_triangles[i].idx[1],
|
|
|
|
idx2 = m_triangles[i].idx[2];
|
|
|
|
const Point &v0 = m_positions[idx0];
|
|
|
|
const Point &v1 = m_positions[idx1];
|
|
|
|
const Point &v2 = m_positions[idx2];
|
|
|
|
const Point2 &uv0 = m_texcoords[idx0];
|
|
|
|
const Point2 &uv1 = m_texcoords[idx1];
|
|
|
|
const Point2 &uv2 = m_texcoords[idx2];
|
|
|
|
|
|
|
|
Vector dP1 = v1 - v0, dP2 = v2 - v0;
|
|
|
|
Vector2 dUV1 = uv1 - uv0, dUV2 = uv2 - uv0;
|
|
|
|
|
|
|
|
Float invDet = 1.0f, determinant = dUV1.x * dUV2.y - dUV1.y * dUV2.x;
|
|
|
|
if (determinant != 0)
|
|
|
|
invDet = 1.0f / determinant;
|
|
|
|
|
|
|
|
Vector dpdu = ( dUV2.y * dP1 - dUV1.y * dP2) * invDet;
|
|
|
|
Vector dpdv = (-dUV2.x * dP1 + dUV1.x * dP2) * invDet;
|
|
|
|
|
|
|
|
if (dpdu.length() == 0.0f) {
|
|
|
|
/* Recovery - required to recover from invalid geometry */
|
|
|
|
Normal n = Normal(cross(v1 - v0, v2 - v0));
|
|
|
|
Float length = n.length();
|
|
|
|
if (length != 0) {
|
|
|
|
n /= length;
|
|
|
|
dpdu = cross(n, dpdv);
|
|
|
|
if (dpdu.length() == 0.0f) {
|
|
|
|
/* At least create some kind of tangent space basis
|
|
|
|
(fair enough for isotropic BxDFs) */
|
|
|
|
coordinateSystem(n, dpdu, dpdv);
|
|
|
|
}
|
2010-08-10 01:38:37 +08:00
|
|
|
} else {
|
2010-10-16 06:33:32 +08:00
|
|
|
zeroArea++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dpdv.length() == 0.0f) {
|
|
|
|
Normal n = Normal(cross(v1 - v0, v2 - v0));
|
|
|
|
Float length = n.length();
|
|
|
|
if (length != 0) {
|
|
|
|
n /= length;
|
|
|
|
dpdv = cross(dpdu, n);
|
|
|
|
if (dpdv.length() == 0.0f) {
|
|
|
|
/* At least create some kind of tangent space basis
|
|
|
|
(fair enough for isotropic BxDFs) */
|
|
|
|
coordinateSystem(n, dpdu, dpdv);
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
2010-10-16 06:33:32 +08:00
|
|
|
} else {
|
|
|
|
zeroArea++;
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
2010-10-16 06:33:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
m_tangents[idx0].dpdu += dpdu;
|
|
|
|
m_tangents[idx1].dpdu += dpdu;
|
|
|
|
m_tangents[idx2].dpdu += dpdu;
|
|
|
|
m_tangents[idx0].dpdv += dpdv;
|
|
|
|
m_tangents[idx1].dpdv += dpdv;
|
|
|
|
m_tangents[idx2].dpdv += dpdv;
|
|
|
|
sharers[idx0]++; sharers[idx1]++; sharers[idx2]++;
|
|
|
|
}
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
/* Orthogonalization + Normalization pass */
|
|
|
|
for (size_t i=0; i<m_vertexCount; i++) {
|
|
|
|
Vector &dpdu = m_tangents[i].dpdu;
|
|
|
|
Vector &dpdv = m_tangents[i].dpdv;
|
|
|
|
|
|
|
|
if (dpdu.lengthSquared() == 0.0f || dpdv.lengthSquared() == 0.0f) {
|
|
|
|
/* At least create some kind of tangent space basis
|
|
|
|
(fair enough for isotropic BxDFs) */
|
|
|
|
coordinateSystem(m_normals[i], dpdu, dpdv);
|
|
|
|
} else {
|
|
|
|
if (sharers[i] > 0) {
|
|
|
|
dpdu /= (Float) sharers[i];
|
|
|
|
dpdv /= (Float) sharers[i];
|
|
|
|
}
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
}
|
2010-10-16 06:33:32 +08:00
|
|
|
delete[] sharers;
|
2010-08-31 06:23:34 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
if (zeroArea > 0 || zeroNormals > 0)
|
|
|
|
Log(EWarn, "\"%s\": computeTangentSpace(): Mesh contains invalid "
|
|
|
|
"geometry: %i zero area triangles and %i zero normals found!",
|
|
|
|
m_name.c_str(), zeroArea, zeroNormals);
|
2010-10-25 23:18:10 +08:00
|
|
|
return true;
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2010-10-25 15:05:30 +08:00
|
|
|
ref<TriMesh> TriMesh::createTriMesh() {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
void TriMesh::serialize(Stream *stream, InstanceManager *manager) const {
|
|
|
|
Shape::serialize(stream, manager);
|
2010-10-16 06:33:32 +08:00
|
|
|
uint32_t flags = 0;
|
|
|
|
if (m_normals)
|
|
|
|
flags |= EHasNormals;
|
|
|
|
if (m_texcoords)
|
|
|
|
flags |= EHasTexcoords;
|
|
|
|
if (m_colors)
|
|
|
|
flags |= EHasColors;
|
|
|
|
if (m_faceNormals)
|
|
|
|
flags |= EFaceNormals;
|
2010-10-17 06:08:55 +08:00
|
|
|
stream->writeString(m_name);
|
|
|
|
m_aabb.serialize(stream);
|
2010-10-16 06:33:32 +08:00
|
|
|
stream->writeUInt(flags);
|
2010-08-10 01:38:37 +08:00
|
|
|
stream->writeULong(m_vertexCount);
|
|
|
|
stream->writeULong(m_triangleCount);
|
2010-10-16 06:33:32 +08:00
|
|
|
|
|
|
|
stream->writeFloatArray(reinterpret_cast<Float *>(m_positions),
|
|
|
|
m_vertexCount * sizeof(Point)/sizeof(Float));
|
|
|
|
if (m_normals)
|
|
|
|
stream->writeFloatArray(reinterpret_cast<Float *>(m_normals),
|
|
|
|
m_vertexCount * sizeof(Normal)/sizeof(Float));
|
|
|
|
if (m_texcoords)
|
|
|
|
stream->writeFloatArray(reinterpret_cast<Float *>(m_texcoords),
|
|
|
|
m_vertexCount * sizeof(Point2)/sizeof(Float));
|
|
|
|
if (m_colors)
|
|
|
|
stream->writeFloatArray(reinterpret_cast<Float *>(m_colors),
|
|
|
|
m_vertexCount * sizeof(Spectrum)/sizeof(Float));
|
|
|
|
stream->writeUIntArray(reinterpret_cast<uint32_t *>(m_triangles),
|
|
|
|
m_triangleCount * sizeof(Triangle)/sizeof(uint32_t));
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2010-10-25 15:05:30 +08:00
|
|
|
void TriMesh::writeOBJ(const fs::path &path) const {
|
|
|
|
fs::ofstream os(path);
|
|
|
|
os << "o " << m_name << endl;
|
|
|
|
for (size_t i=0; i<m_vertexCount; ++i) {
|
|
|
|
os << "v "
|
|
|
|
<< m_positions[i].x << " "
|
|
|
|
<< m_positions[i].y << " "
|
|
|
|
<< m_positions[i].z << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_normals) {
|
|
|
|
for (size_t i=0; i<m_vertexCount; ++i) {
|
|
|
|
os << "vn "
|
|
|
|
<< m_normals[i].x << " "
|
|
|
|
<< m_normals[i].y << " "
|
|
|
|
<< m_normals[i].z << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_normals) {
|
|
|
|
for (size_t i=0; i<m_triangleCount; ++i) {
|
|
|
|
os << "f "
|
|
|
|
<< m_triangles[i].idx[0] + 1 << "//"
|
|
|
|
<< m_triangles[i].idx[0] + 1 << " "
|
|
|
|
<< m_triangles[i].idx[1] + 1 << "//"
|
|
|
|
<< m_triangles[i].idx[1] + 1 << " "
|
|
|
|
<< m_triangles[i].idx[2] + 1 << "//"
|
|
|
|
<< m_triangles[i].idx[2] + 1 << endl;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (size_t i=0; i<m_triangleCount; ++i) {
|
|
|
|
os << "f "
|
|
|
|
<< m_triangles[i].idx[0] + 1 << " "
|
|
|
|
<< m_triangles[i].idx[1] + 1 << " "
|
|
|
|
<< m_triangles[i].idx[2] + 1 << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
os.close();
|
|
|
|
}
|
2010-10-16 06:33:32 +08:00
|
|
|
|
2010-08-31 03:40:32 +08:00
|
|
|
void TriMesh::serialize(Stream *_stream) const {
|
|
|
|
ref<Stream> stream = _stream;
|
|
|
|
|
2010-10-16 08:03:38 +08:00
|
|
|
if (stream->getByteOrder() != Stream::ELittleEndian)
|
2010-08-10 01:38:37 +08:00
|
|
|
Log(EError, "Tried to unserialize a shape from a stream, "
|
2010-10-16 08:03:38 +08:00
|
|
|
"which was not previously set to little endian byte order!");
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
stream->writeShort(MTS_FILEFORMAT_HEADER);
|
|
|
|
stream->writeShort(MTS_FILEFORMAT_VERSION_V3);
|
|
|
|
stream = new ZStream(stream);
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
#if defined(SINGLE_PRECISION)
|
2010-10-16 06:33:32 +08:00
|
|
|
uint32_t flags = ESinglePrecision;
|
2010-08-10 01:38:37 +08:00
|
|
|
#else
|
2010-10-16 06:33:32 +08:00
|
|
|
uint32_t flags = EDoublePrecision;
|
2010-08-10 01:38:37 +08:00
|
|
|
#endif
|
2010-10-16 08:03:38 +08:00
|
|
|
|
2010-10-16 06:33:32 +08:00
|
|
|
if (m_normals)
|
|
|
|
flags |= EHasNormals;
|
|
|
|
if (m_texcoords)
|
|
|
|
flags |= EHasTexcoords;
|
|
|
|
if (m_colors)
|
|
|
|
flags |= EHasColors;
|
|
|
|
if (m_faceNormals)
|
|
|
|
flags |= EFaceNormals;
|
|
|
|
|
|
|
|
stream->writeUInt(flags);
|
2010-08-10 01:38:37 +08:00
|
|
|
stream->writeULong(m_vertexCount);
|
|
|
|
stream->writeULong(m_triangleCount);
|
2010-10-16 06:33:32 +08:00
|
|
|
|
|
|
|
stream->writeFloatArray(reinterpret_cast<Float *>(m_positions),
|
|
|
|
m_vertexCount * sizeof(Point)/sizeof(Float));
|
|
|
|
if (m_normals)
|
|
|
|
stream->writeFloatArray(reinterpret_cast<Float *>(m_normals),
|
|
|
|
m_vertexCount * sizeof(Normal)/sizeof(Float));
|
|
|
|
if (m_texcoords)
|
|
|
|
stream->writeFloatArray(reinterpret_cast<Float *>(m_texcoords),
|
|
|
|
m_vertexCount * sizeof(Point2)/sizeof(Float));
|
|
|
|
if (m_colors)
|
|
|
|
stream->writeFloatArray(reinterpret_cast<Float *>(m_colors),
|
|
|
|
m_vertexCount * sizeof(Spectrum)/sizeof(Float));
|
|
|
|
stream->writeUIntArray(reinterpret_cast<uint32_t *>(m_triangles),
|
|
|
|
m_triangleCount * sizeof(Triangle)/sizeof(uint32_t));
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string TriMesh::toString() const {
|
|
|
|
std::ostringstream oss;
|
|
|
|
oss << getClass()->getName() << "[" << endl
|
|
|
|
<< " name = \"" << m_name<< "\"," << endl
|
|
|
|
<< " triangleCount = " << m_triangleCount << "," << endl
|
|
|
|
<< " vertexCount = " << m_vertexCount << "," << endl
|
2010-10-16 06:33:32 +08:00
|
|
|
<< " faceNormals = " << (m_faceNormals ? "true" : "false") << "," << endl
|
|
|
|
<< " hasNormals = " << (m_normals ? "true" : "false") << "," << endl
|
|
|
|
<< " hasTexcoords = " << (m_texcoords ? "true" : "false") << "," << endl
|
2010-10-25 23:18:10 +08:00
|
|
|
<< " hasTangents = " << (m_tangents ? "true" : "false") << "," << endl
|
2010-10-16 06:33:32 +08:00
|
|
|
<< " hasColors = " << (m_colors ? "true" : "false") << "," << endl
|
2010-08-10 01:38:37 +08:00
|
|
|
<< " surfaceArea = " << m_surfaceArea << "," << endl
|
|
|
|
<< " aabb = " << m_aabb.toString() << "," << endl
|
|
|
|
<< " bsdf = " << indent(m_bsdf.toString()) << "," << endl
|
|
|
|
<< " subsurface = " << indent(m_subsurface.toString()) << "," << endl
|
|
|
|
<< " luminaire = " << indent(m_luminaire.toString()) << endl
|
|
|
|
<< "]";
|
|
|
|
return oss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
MTS_IMPLEMENT_CLASS_S(TriMesh, false, Shape)
|
|
|
|
MTS_NAMESPACE_END
|