realtime preview: render triangle approximations of analytic shapes
parent
9689d85377
commit
a3842d5e2c
|
@ -55,6 +55,9 @@ public:
|
|||
/// Release bound resources
|
||||
void unbind();
|
||||
|
||||
/// Return all bound triangle meshes
|
||||
inline const std::vector<const TriMesh *> &getMeshes() const { return m_meshes; }
|
||||
|
||||
/// Return the shadow cube map for debugging purposes
|
||||
inline GPUTexture *getShadowMap() { return m_shadowMap; }
|
||||
|
||||
|
@ -268,6 +271,7 @@ private:
|
|||
VPLProgramConfiguration m_targetConfig;
|
||||
ref<GPUProgram> m_backgroundProgram;
|
||||
VPLDependencyNode m_backgroundDependencies;
|
||||
std::vector<const TriMesh *> m_meshes;
|
||||
};
|
||||
|
||||
MTS_NAMESPACE_END
|
||||
|
|
|
@ -251,10 +251,22 @@ public:
|
|||
*/
|
||||
virtual const AbstractKDTree *getKDTree() const;
|
||||
|
||||
/**
|
||||
* \brief Create a triangle mesh approximation of this shape
|
||||
*
|
||||
* This function is used by the realtime preview and
|
||||
* certain integrators, which rely on hardware rasterization.
|
||||
*
|
||||
* The default implementation simply returns \a NULL.
|
||||
*/
|
||||
virtual ref<TriMesh> createTriMesh();
|
||||
|
||||
/// Return the shape's BSDF
|
||||
inline const BSDF *getBSDF() const { return m_bsdf.get(); }
|
||||
/// Return the shape's BSDF
|
||||
inline BSDF *getBSDF() { return m_bsdf.get(); }
|
||||
/// Set the BSDF of this shape
|
||||
inline void setBSDF(BSDF *bsdf) { m_bsdf = bsdf; }
|
||||
|
||||
/// Return the name of this shape
|
||||
virtual std::string getName() const;
|
||||
|
@ -272,6 +284,8 @@ public:
|
|||
inline Luminaire *getLuminaire() { return m_luminaire; }
|
||||
/// Return the associated luminaire (if any)
|
||||
inline const Luminaire *getLuminaire() const { return m_luminaire.get(); }
|
||||
/// Set the luminaire of this shape
|
||||
inline void setLuminaire(Luminaire *luminaire) { m_luminaire = luminaire; }
|
||||
|
||||
/// Called once after parsing
|
||||
virtual void configure();
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
#include <mitsuba/core/triangle.h>
|
||||
#include <mitsuba/core/pdf.h>
|
||||
#include <mitsuba/render/shape.h>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -115,6 +118,20 @@ public:
|
|||
|
||||
/// Return the number of vertices
|
||||
inline size_t getVertexCount() const { return m_vertexCount; }
|
||||
|
||||
/**
|
||||
* \brief Create a triangle mesh approximation of this shape
|
||||
*
|
||||
* Since instances are already triangle meshes, the implementation
|
||||
* just returns a pointer to \a this.
|
||||
*/
|
||||
ref<TriMesh> createTriMesh();
|
||||
|
||||
/// Export an Wavefront OBJ version of this file
|
||||
void writeOBJ(const fs::path &path) const;
|
||||
|
||||
/// Return the shape's BSDF
|
||||
inline const BSDF *getBSDF() const { return m_bsdf.get(); }
|
||||
|
||||
/// Sample a point on the mesh
|
||||
Float sampleArea(ShapeSamplingRecord &sRec, const Point2 &sample) const;
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
Transform projectionTransform = camera->getGLProjectionTransform(jitter);
|
||||
m_renderer->setCamera(projectionTransform.getMatrix(), camera->getViewTransform().getMatrix());
|
||||
Transform clipToWorld = camera->getViewTransform().inverse() * projectionTransform.inverse();
|
||||
const std::vector<TriMesh *> meshes = scene->getMeshes();
|
||||
const std::vector<const TriMesh *> meshes = m_shaderManager->getMeshes();
|
||||
Point camPos = scene->getCamera()->getPosition();
|
||||
|
||||
m_renderer->beginDrawingMeshes();
|
||||
|
|
|
@ -115,14 +115,19 @@ void VPLShaderManager::init() {
|
|||
m_altShadowProgramParam_depthVec =
|
||||
m_altShadowProgram->getParameterID("depthVec");
|
||||
|
||||
const std::vector<TriMesh *> meshes = m_scene->getMeshes();
|
||||
const std::vector<Shape *> shapes = m_scene->getShapes();
|
||||
const std::vector<Luminaire *> luminaires = m_scene->getLuminaires();
|
||||
|
||||
for (size_t i=0; i<meshes.size(); ++i) {
|
||||
m_renderer->registerGeometry(meshes[i]);
|
||||
Shader *shader = m_renderer->registerShaderForResource(meshes[i]->getBSDF());
|
||||
for (size_t i=0; i<shapes.size(); ++i) {
|
||||
ref<TriMesh> triMesh = shapes[i]->createTriMesh();
|
||||
if (!triMesh)
|
||||
continue;
|
||||
m_renderer->registerGeometry(triMesh);
|
||||
Shader *shader = m_renderer->registerShaderForResource(triMesh->getBSDF());
|
||||
if (shader != NULL && !shader->isComplete())
|
||||
m_renderer->unregisterShaderForResource(meshes[i]->getBSDF());
|
||||
m_renderer->unregisterShaderForResource(triMesh->getBSDF());
|
||||
triMesh->incRef();
|
||||
m_meshes.push_back(triMesh);
|
||||
}
|
||||
|
||||
for (size_t i=0; i<luminaires.size(); ++i)
|
||||
|
@ -581,13 +586,14 @@ void VPLShaderManager::cleanup() {
|
|||
m_backgroundProgram = NULL;
|
||||
}
|
||||
|
||||
const std::vector<TriMesh *> meshes = m_scene->getMeshes();
|
||||
const std::vector<Luminaire *> luminaires = m_scene->getLuminaires();
|
||||
|
||||
for (size_t i=0; i<meshes.size(); ++i) {
|
||||
m_renderer->unregisterGeometry(meshes[i]);
|
||||
m_renderer->unregisterShaderForResource(meshes[i]->getBSDF());
|
||||
for (size_t i=0; i<m_meshes.size(); ++i) {
|
||||
m_renderer->unregisterGeometry(m_meshes[i]);
|
||||
m_renderer->unregisterShaderForResource(m_meshes[i]->getBSDF());
|
||||
m_meshes[i]->decRef();
|
||||
}
|
||||
m_meshes.clear();
|
||||
for (size_t i=0; i<luminaires.size(); ++i)
|
||||
m_renderer->unregisterShaderForResource(luminaires[i]);
|
||||
m_initialized = false;
|
||||
|
|
|
@ -146,6 +146,9 @@ Float Shape::pdfArea(const ShapeSamplingRecord &sRec) const {
|
|||
return 0.0f;
|
||||
}
|
||||
|
||||
ref<TriMesh> Shape::createTriMesh() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string ShapeSamplingRecord::toString() const {
|
||||
std::ostringstream oss;
|
||||
|
|
|
@ -465,6 +465,10 @@ void TriMesh::computeTangentSpaceBasis() {
|
|||
m_name.c_str(), zeroArea, zeroNormals);
|
||||
}
|
||||
|
||||
ref<TriMesh> TriMesh::createTriMesh() {
|
||||
return this;
|
||||
}
|
||||
|
||||
void TriMesh::serialize(Stream *stream, InstanceManager *manager) const {
|
||||
Shape::serialize(stream, manager);
|
||||
uint32_t flags = 0;
|
||||
|
@ -497,6 +501,46 @@ void TriMesh::serialize(Stream *stream, InstanceManager *manager) const {
|
|||
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<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();
|
||||
}
|
||||
|
||||
void TriMesh::serialize(Stream *_stream) const {
|
||||
ref<Stream> stream = _stream;
|
||||
|
|
|
@ -463,7 +463,7 @@ void PreviewThread::run() {
|
|||
}
|
||||
|
||||
void PreviewThread::oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl) {
|
||||
const std::vector<TriMesh *> meshes = m_context->scene->getMeshes();
|
||||
const std::vector<const TriMesh *> meshes = m_shaderManager->getMeshes();
|
||||
|
||||
m_shaderManager->setVPL(vpl);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <mitsuba/render/bsdf.h>
|
||||
#include <mitsuba/render/subsurface.h>
|
||||
#include <mitsuba/render/luminaire.h>
|
||||
#include <mitsuba/render/trimesh.h>
|
||||
#include <mitsuba/core/properties.h>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
@ -373,24 +374,59 @@ public:
|
|||
return clippedAABB;
|
||||
}
|
||||
|
||||
#if 0
|
||||
inline AABB getAABB(Float start, Float end) const {
|
||||
AABB result;
|
||||
const Float r = m_radius;
|
||||
const Point a = m_objectToWorld(Point(0, 0, start));
|
||||
const Point b = m_objectToWorld(Point(0, 0, end));
|
||||
ref<TriMesh> createTriMesh() {
|
||||
/// Choice of discretization
|
||||
const size_t phiSteps = 20;
|
||||
const Float dPhi = (2*M_PI) / phiSteps;
|
||||
|
||||
ref<TriMesh> mesh = new TriMesh("Cylinder approximation",
|
||||
phiSteps*2, phiSteps*2, true, false, false);
|
||||
|
||||
Point *vertices = mesh->getVertexPositions();
|
||||
Normal *normals = mesh->getVertexNormals();
|
||||
Triangle *triangles = mesh->getTriangles();
|
||||
size_t triangleIdx = 0, vertexIdx = 0;
|
||||
|
||||
for (size_t phi=0; phi<phiSteps; ++phi) {
|
||||
Float sinPhi = std::sin(phi * dPhi);
|
||||
Float cosPhi = std::cos(phi * dPhi);
|
||||
int idx0 = vertexIdx, idx1 = idx0+1;
|
||||
int idx2 = (vertexIdx+2) % (2*phiSteps), idx3 = idx2+1;
|
||||
normals[vertexIdx] = m_objectToWorld(Normal(cosPhi, sinPhi, 0));
|
||||
vertices[vertexIdx++] = m_objectToWorld(Point(cosPhi*m_radius, sinPhi*m_radius, 0));
|
||||
normals[vertexIdx] = m_objectToWorld(Normal(cosPhi, sinPhi, 0));
|
||||
vertices[vertexIdx++] = m_objectToWorld(Point(cosPhi*m_radius, sinPhi*m_radius, m_length));
|
||||
|
||||
triangles[triangleIdx].idx[0] = idx0;
|
||||
triangles[triangleIdx].idx[1] = idx2;
|
||||
triangles[triangleIdx].idx[2] = idx1;
|
||||
triangleIdx++;
|
||||
triangles[triangleIdx].idx[0] = idx1;
|
||||
triangles[triangleIdx].idx[1] = idx2;
|
||||
triangles[triangleIdx].idx[2] = idx3;
|
||||
triangleIdx++;
|
||||
}
|
||||
|
||||
mesh->setBSDF(m_bsdf);
|
||||
mesh->setLuminaire(m_luminaire);
|
||||
mesh->configure();
|
||||
|
||||
return mesh.get();
|
||||
}
|
||||
|
||||
#if 0
|
||||
AABB getAABB() const {
|
||||
const Point a = m_objectToWorld(Point(0, 0, 0));
|
||||
const Point b = m_objectToWorld(Point(0, 0, m_length));
|
||||
|
||||
const Float r = m_radius;
|
||||
AABB result;
|
||||
result.expandBy(a - Vector(r, r, r));
|
||||
result.expandBy(a + Vector(r, r, r));
|
||||
result.expandBy(b - Vector(r, r, r));
|
||||
result.expandBy(b + Vector(r, r, r));
|
||||
return result;
|
||||
}
|
||||
|
||||
AABB getAABB() const {
|
||||
/* Very approximate .. */
|
||||
return getAABB(0, m_length);
|
||||
}
|
||||
#endif
|
||||
|
||||
Float getSurfaceArea() const {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <mitsuba/render/subsurface.h>
|
||||
#include <mitsuba/render/luminaire.h>
|
||||
#include <mitsuba/render/gkdtree.h>
|
||||
#include <mitsuba/render/trimesh.h>
|
||||
#include <mitsuba/core/properties.h>
|
||||
#include <mitsuba/core/fstream.h>
|
||||
#include <mitsuba/core/fresolver.h>
|
||||
|
@ -46,9 +47,10 @@ public:
|
|||
for (size_t i=0; i<m_vertices.size()-1; i++)
|
||||
if (!m_vertexStartsFiber[i+1])
|
||||
m_segIndex.push_back(i);
|
||||
m_segmentCount = m_segIndex.size();
|
||||
|
||||
Log(EDebug, "Building a kd-tree for " SIZE_T_FMT " hair vertices, "
|
||||
SIZE_T_FMT " segments,", m_vertices.size(), m_segIndex.size());
|
||||
SIZE_T_FMT " segments,", m_vertices.size(), m_segmentCount);
|
||||
|
||||
/* Ray-cylinder intersections are expensive. Use only the
|
||||
SAH cost as the tree subdivision stopping criterion,
|
||||
|
@ -91,6 +93,11 @@ public:
|
|||
return m_radius;
|
||||
}
|
||||
|
||||
/// Return the total number of segments
|
||||
inline size_t getSegmentCount() const {
|
||||
return m_segmentCount;
|
||||
}
|
||||
|
||||
/// Intersect a ray with all segments stored in the kd-tree
|
||||
inline bool rayIntersect(const Ray &ray, Float _mint, Float _maxt,
|
||||
Float &t, void *temp) const {
|
||||
|
@ -247,6 +254,7 @@ protected:
|
|||
std::vector<Point> m_vertices;
|
||||
std::vector<bool> m_vertexStartsFiber;
|
||||
std::vector<index_type> m_segIndex;
|
||||
size_t m_segmentCount;
|
||||
Float m_radius;
|
||||
};
|
||||
|
||||
|
@ -366,6 +374,74 @@ public:
|
|||
its.shape = this;
|
||||
}
|
||||
|
||||
ref<TriMesh> createTriMesh() {
|
||||
/// Choice of discretization
|
||||
const size_t phiSteps = 6;
|
||||
const Float dPhi = (2*M_PI) / phiSteps;
|
||||
|
||||
size_t nSegments = m_kdtree->getSegmentCount();
|
||||
ref<TriMesh> mesh = new TriMesh("Hair mesh approximation",
|
||||
phiSteps*2*nSegments, phiSteps*2*nSegments, true, false, false);
|
||||
|
||||
Point *vertices = mesh->getVertexPositions();
|
||||
Normal *normals = mesh->getVertexNormals();
|
||||
Triangle *triangles = mesh->getTriangles();
|
||||
size_t triangleIdx = 0, vertexIdx = 0;
|
||||
|
||||
const std::vector<Point> &hairVertices = m_kdtree->getVertices();
|
||||
const std::vector<bool> &vertexStartsFiber = m_kdtree->getStartFiber();
|
||||
const Float radius = m_kdtree->getRadius();
|
||||
Float *cosPhi = new Float[phiSteps];
|
||||
Float *sinPhi = new Float[phiSteps];
|
||||
for (size_t i=0; i<phiSteps; ++i) {
|
||||
sinPhi[i] = std::sin(i*dPhi);
|
||||
cosPhi[i] = std::cos(i*dPhi);
|
||||
}
|
||||
|
||||
size_t hairIdx = 0;
|
||||
for (size_t iv=0; iv<hairVertices.size()-1; iv++) {
|
||||
if (!vertexStartsFiber[iv+1]) {
|
||||
for (size_t phi=0; phi<phiSteps; ++phi) {
|
||||
Vector tangent = m_kdtree->tangent(iv);
|
||||
Vector dir = Normal(Frame(tangent).toWorld(
|
||||
Vector(cosPhi[phi], sinPhi[phi], 0)));
|
||||
Normal miterNormal1 = m_kdtree->firstMiterNormal(iv);
|
||||
Normal miterNormal2 = m_kdtree->secondMiterNormal(iv);
|
||||
Float t1 = dot(miterNormal1, radius*dir) / dot(miterNormal1, tangent);
|
||||
Float t2 = dot(miterNormal2, radius*dir) / dot(miterNormal2, tangent);
|
||||
|
||||
normals[vertexIdx] = Normal(dir);
|
||||
vertices[vertexIdx++] = m_kdtree->firstVertex(iv) + radius*dir - tangent*t1;
|
||||
normals[vertexIdx] = Normal(dir);
|
||||
vertices[vertexIdx++] = m_kdtree->secondVertex(iv) + radius*dir - tangent*t2;
|
||||
|
||||
int idx0 = 2*(phi + hairIdx*phiSteps), idx1 = idx0+1;
|
||||
int idx2 = (2*phi+2) % (2*phiSteps) + 2*hairIdx*phiSteps, idx3 = idx2+1;
|
||||
triangles[triangleIdx].idx[0] = idx0;
|
||||
triangles[triangleIdx].idx[1] = idx2;
|
||||
triangles[triangleIdx].idx[2] = idx1;
|
||||
triangleIdx++;
|
||||
triangles[triangleIdx].idx[0] = idx1;
|
||||
triangles[triangleIdx].idx[1] = idx2;
|
||||
triangles[triangleIdx].idx[2] = idx3;
|
||||
triangleIdx++;
|
||||
}
|
||||
hairIdx++;
|
||||
}
|
||||
}
|
||||
Assert(triangleIdx == phiSteps*2*nSegments);
|
||||
Assert(vertexIdx == phiSteps*2*nSegments);
|
||||
|
||||
delete[] cosPhi;
|
||||
delete[] sinPhi;
|
||||
|
||||
mesh->setBSDF(m_bsdf);
|
||||
mesh->setLuminaire(m_luminaire);
|
||||
mesh->configure();
|
||||
|
||||
return mesh.get();
|
||||
}
|
||||
|
||||
const AbstractKDTree *getKDTree() const {
|
||||
return m_kdtree.get();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <mitsuba/render/bsdf.h>
|
||||
#include <mitsuba/render/luminaire.h>
|
||||
#include <mitsuba/render/subsurface.h>
|
||||
#include <mitsuba/render/trimesh.h>
|
||||
#include <mitsuba/core/properties.h>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
@ -257,6 +258,93 @@ public:
|
|||
Float cosThetaMax = std::sqrt(std::max((Float) 0, 1 - squareTerm*squareTerm));
|
||||
return squareToConePdf(cosThetaMax);
|
||||
}
|
||||
|
||||
ref<TriMesh> createTriMesh() {
|
||||
/// Choice of discretization
|
||||
const size_t thetaSteps = 20;
|
||||
const size_t phiSteps = thetaSteps * 2;
|
||||
const Float dTheta = M_PI / (thetaSteps-1);
|
||||
const Float dPhi = (2*M_PI) / phiSteps;
|
||||
size_t topIdx = (thetaSteps-2) * phiSteps, botIdx = topIdx+1;
|
||||
|
||||
/// Precompute cosine and sine tables
|
||||
Float *cosPhi = new Float[phiSteps];
|
||||
Float *sinPhi = new Float[phiSteps];
|
||||
for (size_t i=0; i<phiSteps; ++i) {
|
||||
sinPhi[i] = std::sin(i*dPhi);
|
||||
cosPhi[i] = std::cos(i*dPhi);
|
||||
}
|
||||
|
||||
size_t numTris = 2 * phiSteps * (thetaSteps-2);
|
||||
|
||||
ref<TriMesh> mesh = new TriMesh("Sphere approximation",
|
||||
numTris, botIdx+1, true, false, false);
|
||||
|
||||
Point *vertices = mesh->getVertexPositions();
|
||||
Normal *normals = mesh->getVertexNormals();
|
||||
Triangle *triangles = mesh->getTriangles();
|
||||
size_t vertexIdx = 0;
|
||||
for (size_t theta=1; theta<thetaSteps-1; ++theta) {
|
||||
Float sinTheta = std::sin(theta * dTheta);
|
||||
Float cosTheta = std::cos(theta * dTheta);
|
||||
|
||||
for (size_t phi=0; phi<phiSteps; ++phi) {
|
||||
Vector v(
|
||||
sinTheta * cosPhi[phi],
|
||||
sinTheta * sinPhi[phi],
|
||||
cosTheta
|
||||
);
|
||||
vertices[vertexIdx] = m_objectToWorld(Point(v*m_radius));
|
||||
normals[vertexIdx++] = m_objectToWorld(Normal(v));
|
||||
}
|
||||
}
|
||||
vertices[vertexIdx] = m_objectToWorld(Point(0, 0, m_radius));
|
||||
normals[vertexIdx++] = m_objectToWorld(Normal(0, 0, 1));
|
||||
vertices[vertexIdx] = m_objectToWorld(Point(0, 0, -m_radius));
|
||||
normals[vertexIdx++] = m_objectToWorld(Normal(0, 0, -1));
|
||||
Assert(vertexIdx == botIdx+1);
|
||||
|
||||
size_t triangleIdx = 0;
|
||||
for (size_t theta=1; theta<thetaSteps; ++theta) {
|
||||
for (size_t phi=0; phi<phiSteps; ++phi) {
|
||||
size_t nextPhi = (phi + 1) % phiSteps;
|
||||
size_t idx0, idx1, idx2, idx3;
|
||||
if (theta == 1) {
|
||||
idx0 = idx1 = topIdx;
|
||||
} else {
|
||||
idx0 = phiSteps*(theta-2) + phi;
|
||||
idx1 = phiSteps*(theta-2) + nextPhi;
|
||||
}
|
||||
if (theta == thetaSteps-1) {
|
||||
idx2 = idx3 = botIdx;
|
||||
} else {
|
||||
idx2 = phiSteps*(theta-1) + phi;
|
||||
idx3 = phiSteps*(theta-1) + nextPhi;
|
||||
}
|
||||
|
||||
if (idx0 != idx1) {
|
||||
triangles[triangleIdx].idx[0] = idx0;
|
||||
triangles[triangleIdx].idx[1] = idx2;
|
||||
triangles[triangleIdx].idx[2] = idx1;
|
||||
triangleIdx++;
|
||||
}
|
||||
if (idx2 != idx3) {
|
||||
triangles[triangleIdx].idx[0] = idx1;
|
||||
triangles[triangleIdx].idx[1] = idx2;
|
||||
triangles[triangleIdx].idx[2] = idx3;
|
||||
triangleIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert(triangleIdx == numTris);
|
||||
delete[] cosPhi;
|
||||
delete[] sinPhi;
|
||||
mesh->setBSDF(m_bsdf);
|
||||
mesh->setLuminaire(m_luminaire);
|
||||
mesh->configure();
|
||||
|
||||
return mesh.get();
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
|
|
Loading…
Reference in New Issue