Path: blob/master/thirdparty/embree/kernels/subdiv/catmullclark_ring.h
9914 views
// Copyright 2009-2021 Intel Corporation1// SPDX-License-Identifier: Apache-2.023#pragma once45#include "../common/geometry.h"6#include "../common/buffer.h"7#include "half_edge.h"8#include "catmullclark_coefficients.h"910namespace embree11{12struct __aligned(64) FinalQuad {13Vec3fa vtx[4];14};1516template<typename Vertex, typename Vertex_t = Vertex>17struct __aligned(64) CatmullClark1RingT18{19ALIGNED_STRUCT_(64);2021int border_index; //!< edge index where border starts22unsigned int face_valence; //!< number of adjacent quad faces23unsigned int edge_valence; //!< number of adjacent edges (2*face_valence)24float vertex_crease_weight; //!< weight of vertex crease (0 if no vertex crease)25DynamicStackArray<float,16,MAX_RING_FACE_VALENCE> crease_weight; //!< edge crease weights for each adjacent edge26float vertex_level; //!< maximum level of all adjacent edges27float edge_level; //!< level of first edge28unsigned int eval_start_index; //!< topology dependent index to start evaluation29unsigned int eval_unique_identifier; //!< topology dependent unique identifier for this ring30Vertex vtx; //!< center vertex31DynamicStackArray<Vertex,32,MAX_RING_EDGE_VALENCE> ring; //!< ring of neighboring vertices3233public:34CatmullClark1RingT ()35: eval_start_index(0), eval_unique_identifier(0) {} // FIXME: default constructor should be empty3637/*! calculates number of bytes required to serialize this structure */38__forceinline size_t bytes() const39{40size_t ofs = 0;41ofs += sizeof(border_index);42ofs += sizeof(face_valence);43assert(2*face_valence == edge_valence);44ofs += sizeof(vertex_crease_weight);45ofs += face_valence*sizeof(float);46ofs += sizeof(vertex_level);47ofs += sizeof(edge_level);48ofs += sizeof(eval_start_index);49ofs += sizeof(eval_unique_identifier);50ofs += sizeof(vtx);51ofs += edge_valence*sizeof(Vertex);52return ofs;53}5455template<typename Ty>56static __forceinline void store(char* ptr, size_t& ofs, const Ty& v) {57*(Ty*)&ptr[ofs] = v; ofs += sizeof(Ty);58}5960template<typename Ty>61static __forceinline void load(char* ptr, size_t& ofs, Ty& v) {62v = *(Ty*)&ptr[ofs]; ofs += sizeof(Ty);63}6465/*! serializes the ring to some memory location */66__forceinline void serialize(char* ptr, size_t& ofs) const67{68store(ptr,ofs,border_index);69store(ptr,ofs,face_valence);70store(ptr,ofs,vertex_crease_weight);71for (size_t i=0; i<face_valence; i++)72store(ptr,ofs,crease_weight[i]);73store(ptr,ofs,vertex_level);74store(ptr,ofs,edge_level);75store(ptr,ofs,eval_start_index);76store(ptr,ofs,eval_unique_identifier);77Vertex_t::storeu(&ptr[ofs],vtx); ofs += sizeof(Vertex);78for (size_t i=0; i<edge_valence; i++) {79Vertex_t::storeu(&ptr[ofs],ring[i]); ofs += sizeof(Vertex);80}81}8283/*! deserializes the ring from some memory location */84__forceinline void deserialize(char* ptr, size_t& ofs)85{86load(ptr,ofs,border_index);87load(ptr,ofs,face_valence);88edge_valence = 2*face_valence;89load(ptr,ofs,vertex_crease_weight);90for (size_t i=0; i<face_valence; i++)91load(ptr,ofs,crease_weight[i]);92load(ptr,ofs,vertex_level);93load(ptr,ofs,edge_level);94load(ptr,ofs,eval_start_index);95load(ptr,ofs,eval_unique_identifier);96vtx = Vertex_t::loadu(&ptr[ofs]); ofs += sizeof(Vertex);97for (size_t i=0; i<edge_valence; i++) {98ring[i] = Vertex_t::loadu(&ptr[ofs]); ofs += sizeof(Vertex);99}100}101102__forceinline bool hasBorder() const {103return border_index != -1;104}105106__forceinline const Vertex& front(size_t i) const {107assert(edge_valence>i);108return ring[i];109}110111__forceinline const Vertex& back(size_t i) const {112assert(edge_valence>=i);113return ring[edge_valence-i];114}115116__forceinline bool has_last_face() const {117return (size_t)border_index != (size_t)edge_valence-2;118}119120__forceinline bool has_opposite_front(size_t i) const {121return (size_t)border_index != 2*i;122}123124__forceinline bool has_opposite_back(size_t i) const {125return (size_t)border_index != ((size_t)edge_valence-2-2*i);126}127128__forceinline BBox3fa bounds() const129{130BBox3fa bounds ( vtx );131for (size_t i = 0; i<edge_valence ; i++)132bounds.extend( ring[i] );133return bounds;134}135136/*! initializes the ring from the half edge structure */137__forceinline void init(const HalfEdge* const h, const char* vertices, size_t stride)138{139border_index = -1;140vtx = Vertex_t::loadu(vertices+h->getStartVertexIndex()*stride);141vertex_crease_weight = h->vertex_crease_weight;142143HalfEdge* p = (HalfEdge*) h;144145unsigned i=0;146unsigned min_vertex_index = (unsigned)-1;147unsigned min_vertex_index_face = (unsigned)-1;148edge_level = p->edge_level;149vertex_level = 0.0f;150151do152{153vertex_level = max(vertex_level,p->edge_level);154crease_weight[i/2] = p->edge_crease_weight;155assert(p->hasOpposite() || p->edge_crease_weight == float(inf));156157/* store first two vertices of face */158p = p->next();159const unsigned index0 = p->getStartVertexIndex();160ring[i++] = Vertex_t::loadu(vertices+index0*stride);161if (index0 < min_vertex_index) { min_vertex_index = index0; min_vertex_index_face = i>>1; }162p = p->next();163164const unsigned index1 = p->getStartVertexIndex();165ring[i++] = Vertex_t::loadu(vertices+index1*stride);166p = p->next();167168/* continue with next face */169if (likely(p->hasOpposite()))170p = p->opposite();171172/* if there is no opposite go the long way to the other side of the border */173else174{175/* find minimum start vertex */176const unsigned index0 = p->getStartVertexIndex();177if (index0 < min_vertex_index) { min_vertex_index = index0; min_vertex_index_face = i>>1; }178179/*! mark first border edge and store dummy vertex for face between the two border edges */180border_index = i;181crease_weight[i/2] = inf;182ring[i++] = Vertex_t::loadu(vertices+index0*stride);183ring[i++] = vtx; // dummy vertex184185/*! goto other side of border */186p = (HalfEdge*) h;187while (p->hasOpposite())188p = p->opposite()->next();189}190191} while (p != h);192193edge_valence = i;194face_valence = i >> 1;195eval_unique_identifier = min_vertex_index;196eval_start_index = min_vertex_index_face;197198assert( hasValidPositions() );199}200201__forceinline void subdivide(CatmullClark1RingT& dest) const202{203dest.edge_level = 0.5f*edge_level;204dest.vertex_level = 0.5f*vertex_level;205dest.face_valence = face_valence;206dest.edge_valence = edge_valence;207dest.border_index = border_index;208dest.vertex_crease_weight = max(0.0f,vertex_crease_weight-1.0f);209dest.eval_start_index = eval_start_index;210dest.eval_unique_identifier = eval_unique_identifier;211212/* calculate face points */213Vertex_t S = Vertex_t(0.0f);214for (size_t i=0; i<face_valence; i++)215{216size_t face_index = i + eval_start_index; if (face_index >= face_valence) face_index -= face_valence; assert(face_index < face_valence);217size_t index0 = 2*face_index+0; if (index0 >= edge_valence) index0 -= edge_valence; assert(index0 < edge_valence);218size_t index1 = 2*face_index+1; if (index1 >= edge_valence) index1 -= edge_valence; assert(index1 < edge_valence);219size_t index2 = 2*face_index+2; if (index2 >= edge_valence) index2 -= edge_valence; assert(index2 < edge_valence);220S += dest.ring[index1] = ((vtx + ring[index1]) + (ring[index0] + ring[index2])) * 0.25f;221}222223/* calculate new edge points */224size_t num_creases = 0;225array_t<size_t,MAX_RING_FACE_VALENCE> crease_id;226227for (size_t i=0; i<face_valence; i++)228{229size_t face_index = i + eval_start_index;230if (face_index >= face_valence) face_index -= face_valence;231const float edge_crease = crease_weight[face_index];232dest.crease_weight[face_index] = max(edge_crease-1.0f,0.0f);233234size_t index = 2*face_index;235size_t prev_index = face_index == 0 ? edge_valence-1 : 2*face_index-1;236size_t next_index = 2*face_index+1;237238const Vertex_t v = vtx + ring[index];239const Vertex_t f = dest.ring[prev_index] + dest.ring[next_index];240S += ring[index];241242/* fast path for regular edge points */243if (likely(edge_crease <= 0.0f)) {244dest.ring[index] = (v+f) * 0.25f;245}246247/* slower path for hard edge rule */248else {249crease_id[num_creases++] = face_index;250dest.ring[index] = v*0.5f;251252/* even slower path for blended edge rule */253if (unlikely(edge_crease < 1.0f)) {254dest.ring[index] = lerp((v+f)*0.25f,v*0.5f,edge_crease);255}256}257}258259/* compute new vertex using smooth rule */260const float inv_face_valence = 1.0f / (float)face_valence;261const Vertex_t v_smooth = (Vertex_t) madd(inv_face_valence,S,(float(face_valence)-2.0f)*vtx)*inv_face_valence;262dest.vtx = v_smooth;263264/* compute new vertex using vertex_crease_weight rule */265if (unlikely(vertex_crease_weight > 0.0f))266{267if (vertex_crease_weight >= 1.0f) {268dest.vtx = vtx;269} else {270dest.vtx = lerp(v_smooth,vtx,vertex_crease_weight);271}272return;273}274275/* no edge crease rule and dart rule */276if (likely(num_creases <= 1))277return;278279/* compute new vertex using crease rule */280if (likely(num_creases == 2))281{282/* update vertex using crease rule */283const size_t crease0 = crease_id[0], crease1 = crease_id[1];284const Vertex_t v_sharp = (Vertex_t)(ring[2*crease0] + 6.0f*vtx + ring[2*crease1]) * (1.0f / 8.0f);285dest.vtx = v_sharp;286287/* update crease_weights using chaikin rule */288const float crease_weight0 = crease_weight[crease0], crease_weight1 = crease_weight[crease1];289dest.crease_weight[crease0] = max(0.25f*(3.0f*crease_weight0 + crease_weight1)-1.0f,0.0f);290dest.crease_weight[crease1] = max(0.25f*(3.0f*crease_weight1 + crease_weight0)-1.0f,0.0f);291292/* interpolate between sharp and smooth rule */293const float v_blend = 0.5f*(crease_weight0+crease_weight1);294if (unlikely(v_blend < 1.0f)) {295dest.vtx = lerp(v_smooth,v_sharp,v_blend);296}297}298299/* compute new vertex using corner rule */300else {301dest.vtx = vtx;302}303}304305__forceinline bool isRegular1() const306{307if (border_index == -1) {308if (face_valence == 4) return true;309} else {310if (face_valence < 4) return true;311}312return false;313}314315__forceinline size_t numEdgeCreases() const316{317ssize_t numCreases = 0;318for (size_t i=0; i<face_valence; i++) {319numCreases += crease_weight[i] > 0.0f;320}321return numCreases;322}323324enum Type {325TYPE_NONE = 0, //!< invalid type326TYPE_REGULAR = 1, //!< regular patch when ignoring creases327TYPE_REGULAR_CREASES = 2, //!< regular patch when considering creases328TYPE_GREGORY = 4, //!< gregory patch when ignoring creases329TYPE_GREGORY_CREASES = 8, //!< gregory patch when considering creases330TYPE_CREASES = 16 //!< patch has crease features331};332333__forceinline Type type() const334{335/* check if there is an edge crease anywhere */336const size_t numCreases = numEdgeCreases();337const bool noInnerCreases = hasBorder() ? numCreases == 2 : numCreases == 0;338339Type crease_mask = (Type) (TYPE_REGULAR | TYPE_GREGORY);340if (noInnerCreases ) crease_mask = (Type) (crease_mask | TYPE_REGULAR_CREASES | TYPE_GREGORY_CREASES);341if (numCreases != 0) crease_mask = (Type) (crease_mask | TYPE_CREASES);342343/* calculate if this vertex is regular */344bool hasBorder = border_index != -1;345if (face_valence == 2 && hasBorder) {346if (vertex_crease_weight == 0.0f ) return (Type) (crease_mask & (TYPE_REGULAR | TYPE_REGULAR_CREASES | TYPE_GREGORY | TYPE_GREGORY_CREASES | TYPE_CREASES));347else if (vertex_crease_weight == float(inf)) return (Type) (crease_mask & (TYPE_REGULAR | TYPE_REGULAR_CREASES | TYPE_GREGORY | TYPE_GREGORY_CREASES | TYPE_CREASES));348else return TYPE_CREASES;349}350else if (vertex_crease_weight != 0.0f) return TYPE_CREASES;351else if (face_valence == 3 && hasBorder) return (Type) (crease_mask & (TYPE_REGULAR | TYPE_REGULAR_CREASES | TYPE_GREGORY | TYPE_GREGORY_CREASES | TYPE_CREASES));352else if (face_valence == 4 && !hasBorder) return (Type) (crease_mask & (TYPE_REGULAR | TYPE_REGULAR_CREASES | TYPE_GREGORY | TYPE_GREGORY_CREASES | TYPE_CREASES));353else return (Type) (crease_mask & (TYPE_GREGORY | TYPE_GREGORY_CREASES | TYPE_CREASES));354}355356__forceinline bool isFinalResolution(float res) const {357return vertex_level <= res;358}359360/* computes the limit vertex */361__forceinline Vertex getLimitVertex() const362{363/* return hard corner */364if (unlikely(std::isinf(vertex_crease_weight)))365return vtx;366367/* border vertex rule */368if (unlikely(border_index != -1))369{370const unsigned int second_border_index = border_index+2 >= int(edge_valence) ? 0 : border_index+2;371return (4.0f * vtx + (ring[border_index] + ring[second_border_index])) * 1.0f/6.0f;372}373374Vertex_t F( 0.0f );375Vertex_t E( 0.0f );376377assert(eval_start_index < face_valence);378379for (size_t i=0; i<face_valence; i++) {380size_t index = i+eval_start_index;381if (index >= face_valence) index -= face_valence;382F += ring[2*index+1];383E += ring[2*index];384}385386const float n = (float)face_valence;387return (Vertex_t)(n*n*vtx+4.0f*E+F) / ((n+5.0f)*n);388}389390/* gets limit tangent in the direction of edge vtx -> ring[0] */391__forceinline Vertex getLimitTangent() const392{393if (unlikely(std::isinf(vertex_crease_weight)))394return ring[0] - vtx;395396/* border vertex rule */397if (unlikely(border_index != -1))398{399if (border_index != (int)edge_valence-2 ) {400return ring[0] - vtx;401}402else403{404const unsigned int second_border_index = border_index+2 >= int(edge_valence) ? 0 : border_index+2;405return (ring[second_border_index] - ring[border_index]) * 0.5f;406}407}408409Vertex_t alpha( 0.0f );410Vertex_t beta ( 0.0f );411412const size_t n = face_valence;413414assert(eval_start_index < face_valence);415416Vertex_t q( 0.0f );417for (size_t i=0; i<face_valence; i++)418{419size_t index = i+eval_start_index;420if (index >= face_valence) index -= face_valence;421const float a = CatmullClarkPrecomputedCoefficients::table.limittangent_a(index,n);422const float b = CatmullClarkPrecomputedCoefficients::table.limittangent_b(index,n);423alpha += a * ring[2*index];424beta += b * ring[2*index+1];425}426427const float sigma = CatmullClarkPrecomputedCoefficients::table.limittangent_c(n);428return sigma * (alpha + beta);429}430431/* gets limit tangent in the direction of edge vtx -> ring[edge_valence-2] */432__forceinline Vertex getSecondLimitTangent() const433{434if (unlikely(std::isinf(vertex_crease_weight)))435return ring[2] - vtx;436437/* border vertex rule */438if (unlikely(border_index != -1))439{440if (border_index != 2) {441return ring[2] - vtx;442}443else {444const unsigned int second_border_index = border_index+2 >= int(edge_valence) ? 0 : border_index+2;445return (ring[border_index] - ring[second_border_index]) * 0.5f;446}447}448449Vertex_t alpha( 0.0f );450Vertex_t beta ( 0.0f );451452const size_t n = face_valence;453454assert(eval_start_index < face_valence);455456for (size_t i=0; i<face_valence; i++)457{458size_t index = i+eval_start_index;459if (index >= face_valence) index -= face_valence;460461size_t prev_index = index == 0 ? face_valence-1 : index-1; // need to be bit-wise exact in cosf eval462const float a = CatmullClarkPrecomputedCoefficients::table.limittangent_a(prev_index,n);463const float b = CatmullClarkPrecomputedCoefficients::table.limittangent_b(prev_index,n);464alpha += a * ring[2*index];465beta += b * ring[2*index+1];466}467468const float sigma = CatmullClarkPrecomputedCoefficients::table.limittangent_c(n);469return sigma* (alpha + beta);470}471472/* gets surface normal */473const Vertex getNormal() const {474return cross(getLimitTangent(),getSecondLimitTangent());475}476477/* returns center of the n-th quad in the 1-ring */478__forceinline Vertex getQuadCenter(const size_t index) const479{480const Vertex_t &p0 = vtx;481const Vertex_t &p1 = ring[2*index+0];482const Vertex_t &p2 = ring[2*index+1];483const Vertex_t &p3 = index == face_valence-1 ? ring[0] : ring[2*index+2];484const Vertex p = (p0+p1+p2+p3) * 0.25f;485return p;486}487488/* returns center of the n-th edge in the 1-ring */489__forceinline Vertex getEdgeCenter(const size_t index) const {490return (vtx + ring[index*2]) * 0.5f;491}492493bool hasValidPositions() const494{495for (size_t i=0; i<edge_valence; i++) {496if (!isvalid(ring[i]))497return false;498}499return true;500}501502friend __forceinline embree_ostream operator<<(embree_ostream o, const CatmullClark1RingT &c)503{504o << "vtx " << c.vtx << " size = " << c.edge_valence << ", " <<505"hard_edge = " << c.border_index << ", face_valence " << c.face_valence <<506", edge_level = " << c.edge_level << ", vertex_level = " << c.vertex_level << ", eval_start_index: " << c.eval_start_index << ", ring: " << embree_endl;507508for (unsigned int i=0; i<min(c.edge_valence,(unsigned int)MAX_RING_FACE_VALENCE); i++) {509o << i << " -> " << c.ring[i];510if (i % 2 == 0) o << " crease = " << c.crease_weight[i/2];511o << embree_endl;512}513return o;514}515};516517typedef CatmullClark1RingT<Vec3fa,Vec3fa_t> CatmullClark1Ring3fa;518519template<typename Vertex, typename Vertex_t = Vertex>520struct __aligned(64) GeneralCatmullClark1RingT521{522ALIGNED_STRUCT_(64);523524typedef CatmullClark1RingT<Vertex,Vertex_t> CatmullClark1Ring;525526struct Face527{528__forceinline Face() {}529__forceinline Face (int size, float crease_weight)530: size(size), crease_weight(crease_weight) {}531532// FIXME: add member that returns total number of vertices533534int size; // number of vertices-2 of nth face in ring535float crease_weight;536};537538Vertex vtx;539DynamicStackArray<Vertex,32,MAX_RING_EDGE_VALENCE> ring;540DynamicStackArray<Face,16,MAX_RING_FACE_VALENCE> faces;541unsigned int face_valence;542unsigned int edge_valence;543int border_face;544float vertex_crease_weight;545float vertex_level; //!< maximum level of adjacent edges546float edge_level; // level of first edge547bool only_quads; // true if all faces are quads548unsigned int eval_start_face_index;549unsigned int eval_start_vertex_index;550unsigned int eval_unique_identifier;551552public:553GeneralCatmullClark1RingT()554: eval_start_face_index(0), eval_start_vertex_index(0), eval_unique_identifier(0) {}555556__forceinline bool isRegular() const557{558if (border_face == -1 && face_valence == 4) return true;559return false;560}561562__forceinline bool has_last_face() const {563return border_face != (int)face_valence-1;564}565566__forceinline bool has_second_face() const {567return (border_face == -1) || (border_face >= 2);568}569570bool hasValidPositions() const571{572for (size_t i=0; i<edge_valence; i++) {573if (!isvalid(ring[i]))574return false;575}576return true;577}578579__forceinline void init(const HalfEdge* const h, const char* vertices, size_t stride)580{581only_quads = true;582border_face = -1;583vtx = Vertex_t::loadu(vertices+h->getStartVertexIndex()*stride);584vertex_crease_weight = h->vertex_crease_weight;585HalfEdge* p = (HalfEdge*) h;586587unsigned int e=0, f=0;588unsigned min_vertex_index = (unsigned)-1;589unsigned min_vertex_index_face = (unsigned)-1;590unsigned min_vertex_index_vertex = (unsigned)-1;591edge_level = p->edge_level;592vertex_level = 0.0f;593do594{595HalfEdge* p_prev = p->prev();596HalfEdge* p_next = p->next();597const float crease_weight = p->edge_crease_weight;598assert(p->hasOpposite() || p->edge_crease_weight == float(inf));599vertex_level = max(vertex_level,p->edge_level);600601/* find minimum start vertex */602unsigned vertex_index = p_next->getStartVertexIndex();603if (vertex_index < min_vertex_index) { min_vertex_index = vertex_index; min_vertex_index_face = f; min_vertex_index_vertex = e; }604605/* store first N-2 vertices of face */606unsigned int vn = 0;607for (p = p_next; p!=p_prev; p=p->next()) {608ring[e++] = Vertex_t::loadu(vertices+p->getStartVertexIndex()*stride);609vn++;610}611faces[f++] = Face(vn,crease_weight);612only_quads &= (vn == 2);613614/* continue with next face */615if (likely(p->hasOpposite()))616p = p->opposite();617618/* if there is no opposite go the long way to the other side of the border */619else620{621/* find minimum start vertex */622unsigned vertex_index = p->getStartVertexIndex();623if (vertex_index < min_vertex_index) { min_vertex_index = vertex_index; min_vertex_index_face = f; min_vertex_index_vertex = e; }624625/*! mark first border edge and store dummy vertex for face between the two border edges */626border_face = f;627faces[f++] = Face(2,inf);628ring[e++] = Vertex_t::loadu(vertices+p->getStartVertexIndex()*stride);629ring[e++] = vtx; // dummy vertex630631/*! goto other side of border */632p = (HalfEdge*) h;633while (p->hasOpposite())634p = p->opposite()->next();635}636637} while (p != h);638639edge_valence = e;640face_valence = f;641eval_unique_identifier = min_vertex_index;642eval_start_face_index = min_vertex_index_face;643eval_start_vertex_index = min_vertex_index_vertex;644645assert( hasValidPositions() );646}647648__forceinline void subdivide(CatmullClark1Ring& dest) const649{650dest.edge_level = 0.5f*edge_level;651dest.vertex_level = 0.5f*vertex_level;652dest.face_valence = face_valence;653dest.edge_valence = 2*face_valence;654dest.border_index = border_face == -1 ? -1 : 2*border_face; // FIXME:655dest.vertex_crease_weight = max(0.0f,vertex_crease_weight-1.0f);656dest.eval_start_index = eval_start_face_index;657dest.eval_unique_identifier = eval_unique_identifier;658assert(dest.face_valence <= MAX_RING_FACE_VALENCE);659660/* calculate face points */661Vertex_t S = Vertex_t(0.0f);662for (size_t face=0, v=eval_start_vertex_index; face<face_valence; face++) {663size_t f = (face + eval_start_face_index)%face_valence;664665Vertex_t F = vtx;666for (size_t k=v; k<=v+faces[f].size; k++) F += ring[k%edge_valence]; // FIXME: optimize667S += dest.ring[2*f+1] = F/float(faces[f].size+2);668v+=faces[f].size;669v%=edge_valence;670}671672/* calculate new edge points */673size_t num_creases = 0;674array_t<size_t,MAX_RING_FACE_VALENCE> crease_id;675Vertex_t C = Vertex_t(0.0f);676for (size_t face=0, j=eval_start_vertex_index; face<face_valence; face++)677{678size_t i = (face + eval_start_face_index)%face_valence;679680const Vertex_t v = vtx + ring[j];681Vertex_t f = dest.ring[2*i+1];682if (i == 0) f += dest.ring[dest.edge_valence-1];683else f += dest.ring[2*i-1];684S += ring[j];685dest.crease_weight[i] = max(faces[i].crease_weight-1.0f,0.0f);686687/* fast path for regular edge points */688if (likely(faces[i].crease_weight <= 0.0f)) {689dest.ring[2*i] = (v+f) * 0.25f;690}691692/* slower path for hard edge rule */693else {694C += ring[j]; crease_id[num_creases++] = i;695dest.ring[2*i] = v*0.5f;696697/* even slower path for blended edge rule */698if (unlikely(faces[i].crease_weight < 1.0f)) {699dest.ring[2*i] = lerp((v+f)*0.25f,v*0.5f,faces[i].crease_weight);700}701}702j+=faces[i].size;703j%=edge_valence;704}705706/* compute new vertex using smooth rule */707const float inv_face_valence = 1.0f / (float)face_valence;708const Vertex_t v_smooth = (Vertex_t) madd(inv_face_valence,S,(float(face_valence)-2.0f)*vtx)*inv_face_valence;709dest.vtx = v_smooth;710711/* compute new vertex using vertex_crease_weight rule */712if (unlikely(vertex_crease_weight > 0.0f))713{714if (vertex_crease_weight >= 1.0f) {715dest.vtx = vtx;716} else {717dest.vtx = lerp(vtx,v_smooth,vertex_crease_weight);718}719return;720}721722if (likely(num_creases <= 1))723return;724725/* compute new vertex using crease rule */726if (likely(num_creases == 2)) {727const Vertex_t v_sharp = (Vertex_t)(C + 6.0f * vtx) * (1.0f / 8.0f);728const float crease_weight0 = faces[crease_id[0]].crease_weight;729const float crease_weight1 = faces[crease_id[1]].crease_weight;730dest.vtx = v_sharp;731dest.crease_weight[crease_id[0]] = max(0.25f*(3.0f*crease_weight0 + crease_weight1)-1.0f,0.0f);732dest.crease_weight[crease_id[1]] = max(0.25f*(3.0f*crease_weight1 + crease_weight0)-1.0f,0.0f);733const float v_blend = 0.5f*(crease_weight0+crease_weight1);734if (unlikely(v_blend < 1.0f)) {735dest.vtx = lerp(v_sharp,v_smooth,v_blend);736}737}738739/* compute new vertex using corner rule */740else {741dest.vtx = vtx;742}743}744745void convert(CatmullClark1Ring& dst) const746{747dst.edge_level = edge_level;748dst.vertex_level = vertex_level;749dst.vtx = vtx;750dst.face_valence = face_valence;751dst.edge_valence = 2*face_valence;752dst.border_index = border_face == -1 ? -1 : 2*border_face;753for (size_t i=0; i<face_valence; i++)754dst.crease_weight[i] = faces[i].crease_weight;755dst.vertex_crease_weight = vertex_crease_weight;756for (size_t i=0; i<edge_valence; i++) dst.ring[i] = ring[i];757758dst.eval_start_index = eval_start_face_index;759dst.eval_unique_identifier = eval_unique_identifier;760761assert( dst.hasValidPositions() );762}763764765/* gets limit tangent in the direction of edge vtx -> ring[0] */766__forceinline Vertex getLimitTangent() const767{768CatmullClark1Ring cc_vtx;769770/* fast path for quad only rings */771if (only_quads)772{773convert(cc_vtx);774return cc_vtx.getLimitTangent();775}776777subdivide(cc_vtx);778return 2.0f * cc_vtx.getLimitTangent();779}780781/* gets limit tangent in the direction of edge vtx -> ring[edge_valence-2] */782__forceinline Vertex getSecondLimitTangent() const783{784CatmullClark1Ring cc_vtx;785786/* fast path for quad only rings */787if (only_quads)788{789convert(cc_vtx);790return cc_vtx.getSecondLimitTangent();791}792793subdivide(cc_vtx);794return 2.0f * cc_vtx.getSecondLimitTangent();795}796797798/* gets limit vertex */799__forceinline Vertex getLimitVertex() const800{801CatmullClark1Ring cc_vtx;802803/* fast path for quad only rings */804if (only_quads)805convert(cc_vtx);806else807subdivide(cc_vtx);808return cc_vtx.getLimitVertex();809}810811friend __forceinline embree_ostream operator<<(embree_ostream o, const GeneralCatmullClark1RingT &c)812{813o << "vtx " << c.vtx << " size = " << c.edge_valence << ", border_face = " << c.border_face << ", " << " face_valence = " << c.face_valence <<814", edge_level = " << c.edge_level << ", vertex_level = " << c.vertex_level << ", ring: " << embree_endl;815for (size_t v=0, f=0; f<c.face_valence; v+=c.faces[f++].size) {816for (size_t i=v; i<v+c.faces[f].size; i++) {817o << i << " -> " << c.ring[i];818if (i == v) o << " crease = " << c.faces[f].crease_weight;819o << embree_endl;820}821}822return o;823}824};825}826827828