/* 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 . */ #include #include #include #include #include #include #include #include #define MTS_FILEFORMAT_HEADER 0x041C #define MTS_FILEFORMAT_VERSION_V3 0x03 MTS_NAMESPACE_BEGIN TriMesh::TriMesh(const std::string &name, size_t triangleCount, size_t vertexCount, bool hasNormals, bool hasTexcoords, bool hasVertexColors, bool flipNormals, bool faceNormals) : Shape(Properties()), m_triangleCount(triangleCount), m_vertexCount(vertexCount), m_flipNormals(flipNormals), m_faceNormals(faceNormals) { m_name = name; 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; } TriMesh::TriMesh(const Properties &props) : 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 */ m_flipNormals = props.getBoolean("flipNormals", false); m_triangles = NULL; } /* Flags used to identify available data during serialization */ enum ETriMeshFlags { EHasNormals = 0x0001, EHasTexcoords = 0x0002, EHasTangents = 0x0004, EHasColors = 0x0008, EFaceNormals = 0x0010, ESinglePrecision = 0x1000, EDoublePrecision = 0x2000 }; TriMesh::TriMesh(Stream *stream, InstanceManager *manager) : Shape(stream, manager), m_tangents(NULL) { m_name = stream->readString(); m_aabb = AABB(stream); uint32_t flags = stream->readUInt(); m_vertexCount = (size_t) stream->readULong(); m_triangleCount = (size_t) stream->readULong(); m_positions = new Point[m_vertexCount]; stream->readFloatArray(reinterpret_cast(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(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(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(m_colors), m_vertexCount * sizeof(Spectrum)/sizeof(Float)); } else { m_colors = NULL; } m_triangles = new Triangle[m_triangleCount]; stream->readUIntArray(reinterpret_cast(m_triangles), m_triangleCount * sizeof(Triangle)/sizeof(uint32_t)); m_flipNormals = false; configure(); } 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 Double conversion */ float *temp = new float[size]; stream->readSingleArray(temp, size); for (size_t i=0; i stream = _stream; if (stream->getByteOrder() != Stream::ELittleEndian) Log(EError, "Tried to unserialize a shape from a stream, " "which was not previously set to little endian byte order!"); short format = stream->readShort(); if (format == 0x1C04) 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."); if (format != MTS_FILEFORMAT_HEADER) Log(EError, "Encountered an invalid file format!"); short version = stream->readShort(); if (version != MTS_FILEFORMAT_VERSION_V3) Log(EError, "Encountered an incompatible file version!"); stream = new ZStream(stream); uint32_t flags = stream->readUInt(); m_vertexCount = (size_t) stream->readULong(); 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(m_positions), m_vertexCount, sizeof(Point)/sizeof(Float)); if (flags & EHasNormals) { m_normals = new Normal[m_vertexCount]; readHelper(stream, fileDoublePrecision, reinterpret_cast(m_normals), m_vertexCount, sizeof(Normal)/sizeof(Float)); } else { m_normals = NULL; } if (flags & EHasTexcoords) { m_texcoords = new Point2[m_vertexCount]; readHelper(stream, fileDoublePrecision, reinterpret_cast(m_texcoords), m_vertexCount, sizeof(Point2)/sizeof(Float)); } else { m_texcoords = NULL; } if (flags & EHasColors) { m_colors = new Spectrum[m_vertexCount]; readHelper(stream, fileDoublePrecision, reinterpret_cast(m_colors), m_vertexCount, sizeof(Spectrum)/sizeof(Float)); } else { m_colors = NULL; } m_triangles = new Triangle[m_triangleCount]; stream->readUIntArray(reinterpret_cast(m_triangles), m_triangleCount * sizeof(Triangle)/sizeof(uint32_t)); m_flipNormals = false; } TriMesh::~TriMesh() { 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; if (m_triangles) delete[] m_triangles; } 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; } void TriMesh::configure() { Shape::configure(); if (m_areaPDF.isReady()) return; m_aabb.reset(); if (m_triangleCount == 0) Log(EError, "Encountered an empty triangle mesh!"); /* Generate a PDF for sampling wrt. area */ for (size_t i=0; igetType() & BSDF::EAnisotropicMaterial || m_bsdf->usesRayDifferentials()) && !m_tangents) computeTangentSpaceBasis(); } Float TriMesh::getSurfaceArea() const { return m_surfaceArea; } Float TriMesh::sampleArea(ShapeSamplingRecord &sRec, const Point2 &sample) const { Point2 newSeed = sample; int index = m_areaPDF.sampleReuse(newSeed.y); sRec.p = m_triangles[index].sample(m_positions, m_normals, sRec.n, newSeed); return m_invSurfaceArea; } void TriMesh::computeNormals() { int zeroArea = 0, invalidNormals = 0; if (m_faceNormals) { if (m_normals) { delete[] m_normals; m_normals = NULL; } if (m_flipNormals) { /* Change the winding order */ for (size_t i=0; i 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); } bool TriMesh::computeTangentSpaceBasis() { int zeroArea = 0, zeroNormals = 0; 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; } 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 0) { dpdu /= (Float) sharers[i]; dpdv /= (Float) sharers[i]; } } } delete[] sharers; 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); return true; } ref TriMesh::createTriMesh() { return this; } void TriMesh::serialize(Stream *stream, InstanceManager *manager) const { Shape::serialize(stream, manager); uint32_t flags = 0; if (m_normals) flags |= EHasNormals; if (m_texcoords) flags |= EHasTexcoords; if (m_colors) flags |= EHasColors; if (m_faceNormals) flags |= EFaceNormals; stream->writeString(m_name); m_aabb.serialize(stream); stream->writeUInt(flags); stream->writeULong(m_vertexCount); stream->writeULong(m_triangleCount); stream->writeFloatArray(reinterpret_cast(m_positions), m_vertexCount * sizeof(Point)/sizeof(Float)); if (m_normals) stream->writeFloatArray(reinterpret_cast(m_normals), m_vertexCount * sizeof(Normal)/sizeof(Float)); if (m_texcoords) stream->writeFloatArray(reinterpret_cast(m_texcoords), m_vertexCount * sizeof(Point2)/sizeof(Float)); if (m_colors) stream->writeFloatArray(reinterpret_cast(m_colors), m_vertexCount * sizeof(Spectrum)/sizeof(Float)); stream->writeUIntArray(reinterpret_cast(m_triangles), m_triangleCount * sizeof(Triangle)/sizeof(uint32_t)); } void TriMesh::writeOBJ(const fs::path &path) const { fs::ofstream os(path); os << "o " << m_name << endl; for (size_t i=0; i stream = _stream; if (stream->getByteOrder() != Stream::ELittleEndian) Log(EError, "Tried to unserialize a shape from a stream, " "which was not previously set to little endian byte order!"); stream->writeShort(MTS_FILEFORMAT_HEADER); stream->writeShort(MTS_FILEFORMAT_VERSION_V3); stream = new ZStream(stream); #if defined(SINGLE_PRECISION) uint32_t flags = ESinglePrecision; #else uint32_t flags = EDoublePrecision; #endif if (m_normals) flags |= EHasNormals; if (m_texcoords) flags |= EHasTexcoords; if (m_colors) flags |= EHasColors; if (m_faceNormals) flags |= EFaceNormals; stream->writeUInt(flags); stream->writeULong(m_vertexCount); stream->writeULong(m_triangleCount); stream->writeFloatArray(reinterpret_cast(m_positions), m_vertexCount * sizeof(Point)/sizeof(Float)); if (m_normals) stream->writeFloatArray(reinterpret_cast(m_normals), m_vertexCount * sizeof(Normal)/sizeof(Float)); if (m_texcoords) stream->writeFloatArray(reinterpret_cast(m_texcoords), m_vertexCount * sizeof(Point2)/sizeof(Float)); if (m_colors) stream->writeFloatArray(reinterpret_cast(m_colors), m_vertexCount * sizeof(Spectrum)/sizeof(Float)); stream->writeUIntArray(reinterpret_cast(m_triangles), m_triangleCount * sizeof(Triangle)/sizeof(uint32_t)); } std::string TriMesh::toString() const { std::ostringstream oss; oss << getClass()->getName() << "[" << endl << " name = \"" << m_name<< "\"," << endl << " triangleCount = " << m_triangleCount << "," << endl << " vertexCount = " << m_vertexCount << "," << endl << " faceNormals = " << (m_faceNormals ? "true" : "false") << "," << endl << " hasNormals = " << (m_normals ? "true" : "false") << "," << endl << " hasTexcoords = " << (m_texcoords ? "true" : "false") << "," << endl << " hasTangents = " << (m_tangents ? "true" : "false") << "," << endl << " hasColors = " << (m_colors ? "true" : "false") << "," << endl << " 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