2021-05-20 12:49:33 +02:00
|
|
|
// Copyright 2009-2021 Intel Corporation
|
2020-12-19 14:50:20 +01:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "geometry.h"
|
|
|
|
#include "buffer.h"
|
|
|
|
#include "../subdiv/half_edge.h"
|
|
|
|
#include "../subdiv/tessellation_cache.h"
|
|
|
|
#include "../subdiv/catmullclark_coefficients.h"
|
|
|
|
#include "../subdiv/patch.h"
|
|
|
|
#include "../../common/algorithms/parallel_map.h"
|
|
|
|
#include "../../common/algorithms/parallel_set.h"
|
|
|
|
|
|
|
|
namespace embree
|
|
|
|
{
|
|
|
|
class SubdivMesh : public Geometry
|
|
|
|
{
|
|
|
|
ALIGNED_CLASS_(16);
|
|
|
|
public:
|
|
|
|
|
|
|
|
typedef HalfEdge::Edge Edge;
|
|
|
|
|
|
|
|
/*! type of this geometry */
|
|
|
|
static const Geometry::GTypeMask geom_type = Geometry::MTY_SUBDIV_MESH;
|
|
|
|
|
|
|
|
/*! structure used to sort half edges using radix sort by their key */
|
|
|
|
struct KeyHalfEdge
|
|
|
|
{
|
|
|
|
KeyHalfEdge() {}
|
|
|
|
|
|
|
|
KeyHalfEdge (uint64_t key, HalfEdge* edge)
|
|
|
|
: key(key), edge(edge) {}
|
|
|
|
|
|
|
|
__forceinline operator uint64_t() const {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend __forceinline bool operator<(const KeyHalfEdge& e0, const KeyHalfEdge& e1) {
|
|
|
|
return e0.key < e1.key;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
uint64_t key;
|
|
|
|
HalfEdge* edge;
|
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/*! subdiv mesh construction */
|
|
|
|
SubdivMesh(Device* device);
|
|
|
|
|
|
|
|
public:
|
|
|
|
void setMask (unsigned mask);
|
|
|
|
void setSubdivisionMode (unsigned int topologyID, RTCSubdivisionMode mode);
|
|
|
|
void setVertexAttributeTopology(unsigned int vertexAttribID, unsigned int topologyID);
|
|
|
|
void setNumTimeSteps (unsigned int numTimeSteps);
|
|
|
|
void setVertexAttributeCount (unsigned int N);
|
|
|
|
void setTopologyCount (unsigned int N);
|
|
|
|
void setBuffer(RTCBufferType type, unsigned int slot, RTCFormat format, const Ref<Buffer>& buffer, size_t offset, size_t stride, unsigned int num);
|
|
|
|
void* getBuffer(RTCBufferType type, unsigned int slot);
|
|
|
|
void updateBuffer(RTCBufferType type, unsigned int slot);
|
|
|
|
void setTessellationRate(float N);
|
|
|
|
bool verify();
|
|
|
|
void commit();
|
|
|
|
void addElementsToCount (GeometryCounts & counts) const;
|
|
|
|
void setDisplacementFunction (RTCDisplacementFunctionN func);
|
|
|
|
unsigned int getFirstHalfEdge(unsigned int faceID);
|
|
|
|
unsigned int getFace(unsigned int edgeID);
|
|
|
|
unsigned int getNextHalfEdge(unsigned int edgeID);
|
|
|
|
unsigned int getPreviousHalfEdge(unsigned int edgeID);
|
|
|
|
unsigned int getOppositeHalfEdge(unsigned int topologyID, unsigned int edgeID);
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/*! return the number of faces */
|
|
|
|
size_t numFaces() const {
|
|
|
|
return faceVertices.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! return the number of edges */
|
|
|
|
size_t numEdges() const {
|
|
|
|
return topology[0].vertexIndices.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! return the number of vertices */
|
|
|
|
size_t numVertices() const {
|
|
|
|
return vertices[0].size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! calculates the bounds of the i'th subdivision patch at the j'th timestep */
|
|
|
|
__forceinline BBox3fa bounds(size_t i, size_t j = 0) const {
|
|
|
|
return topology[0].getHalfEdge(i)->bounds(vertices[j]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! check if the i'th primitive is valid */
|
|
|
|
__forceinline bool valid(size_t i) const {
|
|
|
|
return topology[0].valid(i) && !invalidFace(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! check if the i'th primitive is valid for the j'th time range */
|
|
|
|
__forceinline bool valid(size_t i, size_t j) const {
|
|
|
|
return topology[0].valid(i) && !invalidFace(i,j);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! prints some statistics */
|
|
|
|
void printStatistics();
|
|
|
|
|
|
|
|
/*! initializes the half edge data structure */
|
|
|
|
void initializeHalfEdgeStructures ();
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/*! returns the vertex buffer for some time step */
|
|
|
|
__forceinline const BufferView<Vec3fa>& getVertexBuffer( const size_t t = 0 ) const {
|
|
|
|
return vertices[t];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns tessellation level of edge */
|
|
|
|
__forceinline float getEdgeLevel(const size_t i) const
|
|
|
|
{
|
|
|
|
if (levels) return clamp(levels[i],1.0f,4096.0f); // FIXME: do we want to limit edge level?
|
|
|
|
else return clamp(tessellationRate,1.0f,4096.0f); // FIXME: do we want to limit edge level?
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
RTCDisplacementFunctionN displFunc; //!< displacement function
|
|
|
|
|
|
|
|
/*! all buffers in this section are provided by the application */
|
|
|
|
public:
|
|
|
|
|
|
|
|
/*! the topology contains all data that may differ when
|
|
|
|
* interpolating different user data buffers */
|
|
|
|
struct Topology
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
/*! Default topology construction */
|
|
|
|
Topology () : halfEdges(nullptr,0) {}
|
|
|
|
|
|
|
|
/*! Topology initialization */
|
|
|
|
Topology (SubdivMesh* mesh);
|
|
|
|
|
|
|
|
/*! make the class movable */
|
|
|
|
public:
|
|
|
|
Topology (Topology&& other) // FIXME: this is only required to workaround compilation issues under Windows
|
|
|
|
: mesh(std::move(other.mesh)),
|
|
|
|
vertexIndices(std::move(other.vertexIndices)),
|
|
|
|
subdiv_mode(std::move(other.subdiv_mode)),
|
|
|
|
halfEdges(std::move(other.halfEdges)),
|
|
|
|
halfEdges0(std::move(other.halfEdges0)),
|
|
|
|
halfEdges1(std::move(other.halfEdges1)) {}
|
|
|
|
|
|
|
|
Topology& operator= (Topology&& other) // FIXME: this is only required to workaround compilation issues under Windows
|
|
|
|
{
|
|
|
|
mesh = std::move(other.mesh);
|
|
|
|
vertexIndices = std::move(other.vertexIndices);
|
|
|
|
subdiv_mode = std::move(other.subdiv_mode);
|
|
|
|
halfEdges = std::move(other.halfEdges);
|
|
|
|
halfEdges0 = std::move(other.halfEdges0);
|
|
|
|
halfEdges1 = std::move(other.halfEdges1);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
/*! check if the i'th primitive is valid in this topology */
|
|
|
|
__forceinline bool valid(size_t i) const
|
|
|
|
{
|
|
|
|
if (unlikely(subdiv_mode == RTC_SUBDIVISION_MODE_NO_BOUNDARY)) {
|
|
|
|
if (getHalfEdge(i)->faceHasBorder()) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! updates the interpolation mode for the topology */
|
|
|
|
void setSubdivisionMode (RTCSubdivisionMode mode);
|
|
|
|
|
|
|
|
/*! marks all buffers as modified */
|
|
|
|
void update ();
|
|
|
|
|
|
|
|
/*! verifies index array */
|
|
|
|
bool verify (size_t numVertices);
|
|
|
|
|
|
|
|
/*! initializes the half edge data structure */
|
|
|
|
void initializeHalfEdgeStructures ();
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
/*! recalculates the half edges */
|
|
|
|
void calculateHalfEdges();
|
|
|
|
|
|
|
|
/*! updates half edges when recalculation is not necessary */
|
|
|
|
void updateHalfEdges();
|
|
|
|
|
|
|
|
/*! user input data */
|
|
|
|
public:
|
|
|
|
|
|
|
|
SubdivMesh* mesh;
|
|
|
|
|
|
|
|
/*! indices of the vertices composing each face */
|
|
|
|
BufferView<unsigned int> vertexIndices;
|
|
|
|
|
|
|
|
/*! subdiv interpolation mode */
|
|
|
|
RTCSubdivisionMode subdiv_mode;
|
|
|
|
|
|
|
|
/*! generated data */
|
|
|
|
public:
|
|
|
|
|
|
|
|
/*! returns the start half edge for face f */
|
|
|
|
__forceinline const HalfEdge* getHalfEdge ( const size_t f ) const {
|
|
|
|
return &halfEdges[mesh->faceStartEdge[f]];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Half edge structure, generated by initHalfEdgeStructures */
|
|
|
|
mvector<HalfEdge> halfEdges;
|
|
|
|
|
|
|
|
/*! the following data is only required during construction of the
|
|
|
|
* half edge structure and can be cleared for static scenes */
|
|
|
|
private:
|
|
|
|
|
|
|
|
/*! two arrays used to sort the half edges */
|
|
|
|
std::vector<KeyHalfEdge> halfEdges0;
|
|
|
|
std::vector<KeyHalfEdge> halfEdges1;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*! returns the start half edge for topology t and face f */
|
|
|
|
__forceinline const HalfEdge* getHalfEdge ( const size_t t , const size_t f ) const {
|
|
|
|
return topology[t].getHalfEdge(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! buffer containing the number of vertices for each face */
|
|
|
|
BufferView<unsigned int> faceVertices;
|
|
|
|
|
|
|
|
/*! array of topologies */
|
|
|
|
vector<Topology> topology;
|
|
|
|
|
|
|
|
/*! vertex buffer (one buffer for each time step) */
|
|
|
|
vector<BufferView<Vec3fa>> vertices;
|
|
|
|
|
|
|
|
/*! user data buffers */
|
|
|
|
vector<RawBufferView> vertexAttribs;
|
|
|
|
|
|
|
|
/*! edge crease buffer containing edges (pairs of vertices) that carry edge crease weights */
|
|
|
|
BufferView<Edge> edge_creases;
|
|
|
|
|
|
|
|
/*! edge crease weights for each edge of the edge_creases buffer */
|
|
|
|
BufferView<float> edge_crease_weights;
|
|
|
|
|
|
|
|
/*! vertex crease buffer containing all vertices that carry vertex crease weights */
|
|
|
|
BufferView<unsigned int> vertex_creases;
|
|
|
|
|
|
|
|
/*! vertex crease weights for each vertex of the vertex_creases buffer */
|
|
|
|
BufferView<float> vertex_crease_weights;
|
|
|
|
|
|
|
|
/*! subdivision level for each half edge of the vertexIndices buffer */
|
|
|
|
BufferView<float> levels;
|
|
|
|
float tessellationRate; // constant rate that is used when levels is not set
|
|
|
|
|
|
|
|
/*! buffer that marks specific faces as holes */
|
|
|
|
BufferView<unsigned> holes;
|
|
|
|
|
|
|
|
/*! all data in this section is generated by initializeHalfEdgeStructures function */
|
|
|
|
private:
|
|
|
|
|
|
|
|
/*! number of half edges used by faces */
|
|
|
|
size_t numHalfEdges;
|
|
|
|
|
|
|
|
/*! fast lookup table to find the first half edge for some face */
|
|
|
|
mvector<uint32_t> faceStartEdge;
|
|
|
|
|
|
|
|
/*! fast lookup table to find the face for some half edge */
|
|
|
|
mvector<uint32_t> halfEdgeFace;
|
|
|
|
|
|
|
|
/*! set with all holes */
|
|
|
|
parallel_set<uint32_t> holeSet;
|
|
|
|
|
|
|
|
/*! fast lookup table to detect invalid faces */
|
2021-05-20 12:49:33 +02:00
|
|
|
mvector<char> invalid_face;
|
2020-12-19 14:50:20 +01:00
|
|
|
|
|
|
|
/*! test if face i is invalid in timestep j */
|
2021-05-20 12:49:33 +02:00
|
|
|
__forceinline char& invalidFace(size_t i, size_t j = 0) { return invalid_face[i*numTimeSteps+j]; }
|
|
|
|
__forceinline const char& invalidFace(size_t i, size_t j = 0) const { return invalid_face[i*numTimeSteps+j]; }
|
2020-12-19 14:50:20 +01:00
|
|
|
|
|
|
|
/*! interpolation cache */
|
|
|
|
public:
|
|
|
|
static __forceinline size_t numInterpolationSlots4(size_t stride) { return (stride+15)/16; }
|
|
|
|
static __forceinline size_t numInterpolationSlots8(size_t stride) { return (stride+31)/32; }
|
|
|
|
static __forceinline size_t interpolationSlot(size_t prim, size_t slot, size_t stride) {
|
|
|
|
const size_t slots = numInterpolationSlots4(stride);
|
|
|
|
assert(slot < slots);
|
|
|
|
return slots*prim+slot;
|
|
|
|
}
|
|
|
|
std::vector<std::vector<SharedLazyTessellationCache::CacheEntry>> vertex_buffer_tags;
|
|
|
|
std::vector<std::vector<SharedLazyTessellationCache::CacheEntry>> vertex_attrib_buffer_tags;
|
|
|
|
std::vector<Patch3fa::Ref> patch_eval_trees;
|
|
|
|
|
|
|
|
/*! the following data is only required during construction of the
|
|
|
|
* half edge structure and can be cleared for static scenes */
|
|
|
|
private:
|
|
|
|
|
|
|
|
/*! map with all vertex creases */
|
|
|
|
parallel_map<uint32_t,float> vertexCreaseMap;
|
|
|
|
|
|
|
|
/*! map with all edge creases */
|
|
|
|
parallel_map<uint64_t,float> edgeCreaseMap;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
/*! counts number of geometry commits */
|
|
|
|
size_t commitCounter;
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace isa
|
|
|
|
{
|
|
|
|
struct SubdivMeshISA : public SubdivMesh
|
|
|
|
{
|
|
|
|
SubdivMeshISA (Device* device)
|
|
|
|
: SubdivMesh(device) {}
|
|
|
|
|
|
|
|
void interpolate(const RTCInterpolateArguments* const args);
|
|
|
|
void interpolateN(const RTCInterpolateNArguments* const args);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_ISA_FUNCTION(SubdivMesh*, createSubdivMesh, Device*);
|
|
|
|
};
|