mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-03 16:31:04 +00:00
404ec9649d
For face-varying interpolation, sharpness should be interpreted as an infintely sharp boundary. Hbr implements this correctly for vertex sharpness, but inconsistently for edge sharpness: both the fvarbits cache and methods that gather face-varying sharpness need to be corrected. fixes #231
741 lines
24 KiB
C++
741 lines
24 KiB
C++
//
|
|
// Copyright 2013 Pixar
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
// with the following modification; you may not use this file except in
|
|
// compliance with the Apache License and the following modification to it:
|
|
// Section 6. Trademarks. is deleted and replaced with:
|
|
//
|
|
// 6. Trademarks. This License does not grant permission to use the trade
|
|
// names, trademarks, service marks, or product names of the Licensor
|
|
// and its affiliates, except as required to comply with Section 4(c) of
|
|
// the License and to reproduce the content of the NOTICE file.
|
|
//
|
|
// You may obtain a copy of the Apache License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the Apache License with the above modification is
|
|
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the Apache License for the specific
|
|
// language governing permissions and limitations under the Apache License.
|
|
//
|
|
|
|
#ifndef HBRHALFEDGE_H
|
|
#define HBRHALFEDGE_H
|
|
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
|
|
|
|
#ifdef HBRSTITCH
|
|
#include "libgprims/stitch.h"
|
|
#include "libgprims/stitchInternal.h"
|
|
#endif
|
|
|
|
#include "../version.h"
|
|
|
|
namespace OpenSubdiv {
|
|
namespace OPENSUBDIV_VERSION {
|
|
|
|
template <class T> class HbrFace;
|
|
template <class T> class HbrHalfedge;
|
|
template <class T> class HbrVertex;
|
|
template <class T> class HbrMesh;
|
|
|
|
template <class T> std::ostream& operator<<(std::ostream& out, const HbrHalfedge<T>& edge);
|
|
|
|
template <class T> class HbrHalfedge {
|
|
|
|
private:
|
|
HbrHalfedge(): opposite(0), incidentVertex(-1), vchild(-1), sharpness(0.0f)
|
|
#ifdef HBRSTITCH
|
|
, stitchccw(1), raystitchccw(1)
|
|
#endif
|
|
, coarse(1)
|
|
{
|
|
}
|
|
|
|
HbrHalfedge(const HbrHalfedge &e) {}
|
|
|
|
~HbrHalfedge();
|
|
|
|
void Clear();
|
|
|
|
// Finish the initialization of the halfedge. Should only be
|
|
// called by HbrFace
|
|
void Initialize(HbrHalfedge<T>* opposite, int index, HbrVertex<T>* origin, unsigned int *fvarbits, HbrFace<T>* face);
|
|
public:
|
|
|
|
// Returns the opposite half edge
|
|
HbrHalfedge<T>* GetOpposite() const { return opposite; }
|
|
|
|
// Sets the opposite half edge
|
|
void SetOpposite(HbrHalfedge<T>* opposite) { this->opposite = opposite; sharpness = opposite->sharpness; }
|
|
|
|
// Returns the next clockwise halfedge around the incident face
|
|
HbrHalfedge<T>* GetNext() const {
|
|
if (m_index == 4) {
|
|
const size_t edgesize = sizeof(HbrHalfedge<T>) + sizeof(HbrFace<T>*);
|
|
if (lastedge) {
|
|
return (HbrHalfedge<T>*) ((char*) this - (GetFace()->GetNumVertices() - 1) * edgesize);
|
|
} else {
|
|
return (HbrHalfedge<T>*) ((char*) this + edgesize);
|
|
}
|
|
} else {
|
|
if (lastedge) {
|
|
return (HbrHalfedge<T>*) ((char*) this - (m_index) * sizeof(HbrHalfedge<T>));
|
|
} else {
|
|
return (HbrHalfedge<T>*) ((char*) this + sizeof(HbrHalfedge<T>));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns the previous counterclockwise halfedge around the incident face
|
|
HbrHalfedge<T>* GetPrev() const {
|
|
const size_t edgesize = (m_index == 4) ?
|
|
(sizeof(HbrHalfedge<T>) + sizeof(HbrFace<T>*)) :
|
|
sizeof(HbrHalfedge<T>);
|
|
if (firstedge) {
|
|
return (HbrHalfedge<T>*) ((char*) this + (GetFace()->GetNumVertices() - 1) * edgesize);
|
|
} else {
|
|
return (HbrHalfedge<T>*) ((char*) this - edgesize);
|
|
}
|
|
}
|
|
|
|
// Returns the incident vertex
|
|
HbrVertex<T>* GetVertex() const {
|
|
return GetMesh()->GetVertex(incidentVertex);
|
|
}
|
|
|
|
// Returns the incident vertex
|
|
HbrVertex<T>* GetVertex(HbrMesh<T> *mesh) const {
|
|
return mesh->GetVertex(incidentVertex);
|
|
}
|
|
|
|
// Returns the incident vertex
|
|
int GetVertexID() const {
|
|
return incidentVertex;
|
|
}
|
|
|
|
// Returns the source vertex
|
|
HbrVertex<T>* GetOrgVertex() const {
|
|
return GetVertex();
|
|
}
|
|
|
|
// Returns the source vertex
|
|
HbrVertex<T>* GetOrgVertex(HbrMesh<T> *mesh) const {
|
|
return GetVertex(mesh);
|
|
}
|
|
|
|
// Returns the source vertex id
|
|
int GetOrgVertexID() const {
|
|
return incidentVertex;
|
|
}
|
|
|
|
// Changes the origin vertex. Generally not a good idea to do
|
|
void SetOrgVertex(HbrVertex<T>* v) { incidentVertex = v->GetID(); }
|
|
|
|
// Returns the destination vertex
|
|
HbrVertex<T>* GetDestVertex() const { return GetNext()->GetOrgVertex(); }
|
|
|
|
// Returns the destination vertex
|
|
HbrVertex<T>* GetDestVertex(HbrMesh<T> *mesh) const { return GetNext()->GetOrgVertex(mesh); }
|
|
|
|
// Returns the destination vertex ID
|
|
int GetDestVertexID() const { return GetNext()->GetOrgVertexID(); }
|
|
|
|
// Returns the incident facet
|
|
HbrFace<T>* GetFace() const {
|
|
if (m_index == 4) {
|
|
// Pointer to face is stored after the data for the edge
|
|
return *(HbrFace<T>**)((char *) this + sizeof(HbrHalfedge<T>));
|
|
} else {
|
|
return (HbrFace<T>*) ((char*) this - (m_index) * sizeof(HbrHalfedge<T>) -
|
|
offsetof(HbrFace<T>, edges));
|
|
}
|
|
}
|
|
|
|
// Returns the mesh to which this edge belongs
|
|
HbrMesh<T>* GetMesh() const { return GetFace()->GetMesh(); }
|
|
|
|
// Returns the face on the right
|
|
HbrFace<T>* GetRightFace() const { return opposite ? opposite->GetLeftFace() : NULL; }
|
|
|
|
// Return the face on the left of the halfedge
|
|
HbrFace<T>* GetLeftFace() const { return GetFace(); }
|
|
|
|
// Returns whether this is a boundary edge
|
|
bool IsBoundary() const { return opposite == 0; }
|
|
|
|
// Tag the edge as being an infinitely sharp facevarying edge
|
|
void SetFVarInfiniteSharp(int datum, bool infsharp) {
|
|
int intindex = datum >> 4;
|
|
unsigned int bits = infsharp << ((datum & 15) * 2);
|
|
getFVarInfSharp()[intindex] |= bits;
|
|
if (opposite) {
|
|
opposite->getFVarInfSharp()[intindex] |= bits;
|
|
}
|
|
}
|
|
|
|
// Copy fvar infinite sharpness flags from another edge
|
|
void CopyFVarInfiniteSharpness(HbrHalfedge<T>* edge) {
|
|
unsigned int *fvarinfsharp = getFVarInfSharp();
|
|
if (fvarinfsharp) {
|
|
const int fvarcount = GetMesh()->GetFVarCount();
|
|
int fvarbitsSizePerEdge = ((fvarcount + 15) / 16);
|
|
|
|
if (edge->IsSharp(true)) {
|
|
memset(fvarinfsharp, 0x55555555, fvarbitsSizePerEdge * sizeof(unsigned int));
|
|
} else {
|
|
memcpy(fvarinfsharp, edge->getFVarInfSharp(), fvarbitsSizePerEdge * sizeof(unsigned int));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns whether the edge is infinitely sharp in facevarying for
|
|
// a particular facevarying datum
|
|
bool GetFVarInfiniteSharp(int datum);
|
|
|
|
// Returns whether the edge is infinitely sharp in any facevarying
|
|
// datum
|
|
bool IsFVarInfiniteSharpAnywhere();
|
|
|
|
// Get the sharpness relative to facevarying data
|
|
float GetFVarSharpness(int datum, bool ignoreGeometry=false);
|
|
|
|
// Returns the (raw) sharpness of the edge
|
|
float GetSharpness() const { return sharpness; }
|
|
|
|
// Sets the sharpness of the edge
|
|
void SetSharpness(float sharp) { sharpness = sharp; if (opposite) opposite->sharpness = sharp; ClearMask(); }
|
|
|
|
// Returns whether the edge is sharp at the current level of
|
|
// subdivision (next = false) or at the next level of subdivision
|
|
// (next = true).
|
|
bool IsSharp(bool next) const { return (next ? (sharpness > 0.0f) : (sharpness >= 1.0f)); }
|
|
|
|
// Clears the masks of the adjacent edge vertices. Usually called
|
|
// when a change in edge sharpness occurs.
|
|
void ClearMask() { GetOrgVertex()->ClearMask(); GetDestVertex()->ClearMask(); }
|
|
|
|
// Subdivide the edge into a vertex if needed and return
|
|
HbrVertex<T>* Subdivide();
|
|
|
|
// Make sure the edge has its opposite face
|
|
void GuaranteeNeighbor();
|
|
|
|
// True if the edge has a subdivided child vertex
|
|
bool HasChild() const { return vchild!=-1; }
|
|
|
|
// Remove the reference to subdivided vertex
|
|
void RemoveChild() { vchild = -1; }
|
|
|
|
// Sharpness constants
|
|
enum Mask {
|
|
k_Smooth = 0,
|
|
k_Sharp = 1,
|
|
k_InfinitelySharp = 10
|
|
};
|
|
|
|
#ifdef HBRSTITCH
|
|
StitchEdge* GetStitchEdge(int i) {
|
|
StitchEdge **stitchEdge = getStitchEdges();
|
|
// If the stitch edge exists, the ownership is transferred to
|
|
// the caller. Make sure the opposite edge loses ownership as
|
|
// well.
|
|
if (stitchEdge[i]) {
|
|
if (opposite) {
|
|
opposite->getStitchEdges()[i] = 0;
|
|
}
|
|
return StitchGetEdge(&stitchEdge[i]);
|
|
}
|
|
// If the stitch edge does not exist then we create one now.
|
|
// Make sure the opposite edge gets a copy of it too
|
|
else {
|
|
StitchGetEdge(&stitchEdge[i]);
|
|
if (opposite) {
|
|
opposite->getStitchEdges()[i] = stitchEdge[i];
|
|
}
|
|
return stitchEdge[i];
|
|
}
|
|
}
|
|
|
|
// If stitch edge exists, and this edge has no opposite, destroy
|
|
// it
|
|
void DestroyStitchEdges(int stitchcount) {
|
|
if (!opposite) {
|
|
StitchEdge **stitchEdge = getStitchEdges();
|
|
for (int i = 0; i < stitchcount; ++i) {
|
|
if (stitchEdge[i]) {
|
|
StitchFreeEdge(stitchEdge[i]);
|
|
stitchEdge[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StitchEdge* GetRayStitchEdge(int i) {
|
|
return GetStitchEdge(i + 2);
|
|
}
|
|
|
|
// Splits our split edge between our children. We'd better have
|
|
// subdivided this edge by this point
|
|
void SplitStitchEdge(int i) {
|
|
StitchEdge* se = GetStitchEdge(i);
|
|
HbrHalfedge<T>* ea = GetOrgVertex()->Subdivide()->GetEdge(Subdivide());
|
|
HbrHalfedge<T>* eb = Subdivide()->GetEdge(GetDestVertex()->Subdivide());
|
|
StitchEdge **ease = ea->getStitchEdges();
|
|
StitchEdge **ebse = eb->getStitchEdges();
|
|
if (i >= 2) { // ray tracing stitches
|
|
if (!raystitchccw) {
|
|
StitchSplitEdge(se, &ease[i], &ebse[i], false, 0, 0, 0);
|
|
} else {
|
|
StitchSplitEdge(se, &ebse[i], &ease[i], true, 0, 0, 0);
|
|
}
|
|
ea->raystitchccw = eb->raystitchccw = raystitchccw;
|
|
if (eb->opposite) {
|
|
eb->opposite->getStitchEdges()[i] = ebse[i];
|
|
eb->opposite->raystitchccw = raystitchccw;
|
|
}
|
|
if (ea->opposite) {
|
|
ea->opposite->getStitchEdges()[i] = ease[i];
|
|
ea->opposite->raystitchccw = raystitchccw;
|
|
}
|
|
} else {
|
|
if (!stitchccw) {
|
|
StitchSplitEdge(se, &ease[i], &ebse[i], false, 0, 0, 0);
|
|
} else {
|
|
StitchSplitEdge(se, &ebse[i], &ease[i], true, 0, 0, 0);
|
|
}
|
|
ea->stitchccw = eb->stitchccw = stitchccw;
|
|
if (eb->opposite) {
|
|
eb->opposite->getStitchEdges()[i] = ebse[i];
|
|
eb->opposite->stitchccw = stitchccw;
|
|
}
|
|
if (ea->opposite) {
|
|
ea->opposite->getStitchEdges()[i] = ease[i];
|
|
ea->opposite->stitchccw = stitchccw;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SplitRayStitchEdge(int i) {
|
|
SplitStitchEdge(i + 2);
|
|
}
|
|
|
|
void SetStitchEdge(int i, StitchEdge* edge) {
|
|
StitchEdge **stitchEdges = getStitchEdges();
|
|
stitchEdges[i] = edge;
|
|
if (opposite) {
|
|
opposite->getStitchEdges()[i] = edge;
|
|
}
|
|
}
|
|
|
|
void SetRayStitchEdge(int i, StitchEdge* edge) {
|
|
StitchEdge **stitchEdges = getStitchEdges();
|
|
stitchEdges[i+2] = edge;
|
|
if (opposite) {
|
|
opposite->getStitchEdges()[i+2] = edge;
|
|
}
|
|
}
|
|
|
|
void* GetStitchData() const {
|
|
if (stitchdatavalid) return GetMesh()->GetStitchData(this);
|
|
else return 0;
|
|
}
|
|
|
|
void SetStitchData(void* data) {
|
|
GetMesh()->SetStitchData(this, data);
|
|
stitchdatavalid = data ? 1 : 0;
|
|
if (opposite) {
|
|
opposite->GetMesh()->SetStitchData(opposite, data);
|
|
opposite->stitchdatavalid = stitchdatavalid;
|
|
}
|
|
}
|
|
|
|
bool GetStitchCCW(bool raytraced) const { return raytraced ? raystitchccw : stitchccw; }
|
|
|
|
void ClearStitchCCW(bool raytraced) {
|
|
if (raytraced) {
|
|
raystitchccw = 0;
|
|
if (opposite) opposite->raystitchccw = 0;
|
|
} else {
|
|
stitchccw = 0;
|
|
if (opposite) opposite->stitchccw = 0;
|
|
}
|
|
}
|
|
|
|
void ToggleStitchCCW(bool raytraced) {
|
|
if (raytraced) {
|
|
raystitchccw = 1 - raystitchccw;
|
|
if (opposite) opposite->raystitchccw = raystitchccw;
|
|
} else {
|
|
stitchccw = 1 - stitchccw;
|
|
if (opposite) opposite->stitchccw = stitchccw;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
// Marks the edge as being "coarse" (belonging to the control
|
|
// mesh). Generally this distinction only needs to be made if
|
|
// we're worried about interpolateboundary behaviour
|
|
void SetCoarse(bool c) { coarse = c; }
|
|
bool IsCoarse() const { return coarse; }
|
|
|
|
friend class HbrFace<T>;
|
|
|
|
private:
|
|
HbrHalfedge<T>* opposite;
|
|
// Index of incident vertex
|
|
int incidentVertex;
|
|
|
|
// Index of subdivided vertex child
|
|
int vchild;
|
|
float sharpness;
|
|
|
|
#ifdef HBRSTITCH
|
|
unsigned short stitchccw:1;
|
|
unsigned short raystitchccw:1;
|
|
unsigned short stitchdatavalid:1;
|
|
#endif
|
|
unsigned short coarse:1;
|
|
unsigned short lastedge:1;
|
|
unsigned short firstedge:1;
|
|
|
|
// If m_index = 0, 1, 2 or 3: we are the m_index edge of an
|
|
// incident face with 3 or 4 vertices.
|
|
// If m_index = 4: our incident face has more than 4 vertices, and
|
|
// we must do some extra math to determine what our actual index
|
|
// is. See getIndex()
|
|
unsigned short m_index:3;
|
|
|
|
// Returns the index of the edge relative to its incident face.
|
|
// This relies on knowledge of the face's edge allocation pattern
|
|
int getIndex() const {
|
|
if (m_index < 4) {
|
|
return m_index;
|
|
} else {
|
|
// We allocate room for up to 4 values (to handle tri or
|
|
// quad) in the edges array. If there are more than that,
|
|
// they _all_ go in the faces' extraedges array.
|
|
HbrFace<T>* incidentFace = *(HbrFace<T>**)((char *) this + sizeof(HbrHalfedge<T>));
|
|
return int(((char *) this - incidentFace->extraedges) /
|
|
(sizeof(HbrHalfedge<T>) + sizeof(HbrFace<T>*)));
|
|
}
|
|
}
|
|
|
|
// Returns bitmask indicating whether a given facevarying datum
|
|
// for the edge is infinitely sharp. Each datum has two bits, and
|
|
// if those two bits are set to 3, it means the status has not
|
|
// been computed yet.
|
|
unsigned int *getFVarInfSharp() {
|
|
unsigned int *fvarbits = GetFace()->fvarbits;
|
|
if (fvarbits) {
|
|
int fvarbitsSizePerEdge = ((GetMesh()->GetFVarCount() + 15) / 16);
|
|
return fvarbits + getIndex() * fvarbitsSizePerEdge;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#ifdef HBRSTITCH
|
|
StitchEdge **getStitchEdges() {
|
|
return GetFace()->stitchEdges + GetMesh()->GetStitchCount() * getIndex();
|
|
}
|
|
#endif
|
|
|
|
#ifdef HBR_ADAPTIVE
|
|
public:
|
|
struct adaptiveFlags {
|
|
unsigned isTransition:1;
|
|
unsigned isTriangleHead:1;
|
|
unsigned isWatertightCritical:1;
|
|
|
|
adaptiveFlags() : isTransition(0),isTriangleHead(0),isWatertightCritical(0) { }
|
|
};
|
|
|
|
adaptiveFlags _adaptiveFlags;
|
|
|
|
bool IsInsideHole() const {
|
|
|
|
HbrFace<T> * left = GetLeftFace();
|
|
if (left and (not left->IsHole()))
|
|
return false;
|
|
|
|
HbrFace<T> * right = GetRightFace();
|
|
if (right and (not right->IsHole()))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsTransition() const { return _adaptiveFlags.isTransition; }
|
|
|
|
bool IsTriangleHead() const { return _adaptiveFlags.isTriangleHead; }
|
|
|
|
bool IsWatertightCritical() const { return _adaptiveFlags.isWatertightCritical; }
|
|
#endif
|
|
};
|
|
|
|
template <class T>
|
|
void
|
|
HbrHalfedge<T>::Initialize(HbrHalfedge<T>* opposite, int index, HbrVertex<T>* origin,
|
|
unsigned int *fvarbits, HbrFace<T>* face) {
|
|
HbrMesh<T> *mesh = face->GetMesh();
|
|
if (face->GetNumVertices() <= 4) {
|
|
m_index = index;
|
|
} else {
|
|
m_index = 4;
|
|
// Assumes upstream allocation ensured we have extra storage
|
|
// for pointer to face after the halfedge data structure
|
|
// itself
|
|
*(HbrFace<T>**)((char *) this + sizeof(HbrHalfedge<T>)) = face;
|
|
}
|
|
|
|
this->opposite = opposite;
|
|
incidentVertex = origin->GetID();
|
|
lastedge = (index == face->GetNumVertices() - 1);
|
|
firstedge = (index == 0);
|
|
if (opposite) {
|
|
sharpness = opposite->sharpness;
|
|
#ifdef HBRSTITCH
|
|
StitchEdge **stitchEdges = face->stitchEdges +
|
|
mesh->GetStitchCount() * index;
|
|
for (int i = 0; i < mesh->GetStitchCount(); ++i) {
|
|
stitchEdges[i] = opposite->getStitchEdges()[i];
|
|
}
|
|
stitchccw = opposite->stitchccw;
|
|
raystitchccw = opposite->raystitchccw;
|
|
stitchdatavalid = 0;
|
|
if (stitchEdges && opposite->GetStitchData()) {
|
|
mesh->SetStitchData(this, opposite->GetStitchData());
|
|
stitchdatavalid = 1;
|
|
}
|
|
#endif
|
|
if (fvarbits) {
|
|
const int fvarcount = mesh->GetFVarCount();
|
|
int fvarbitsSizePerEdge = ((fvarcount + 15) / 16);
|
|
memcpy(fvarbits, opposite->getFVarInfSharp(), fvarbitsSizePerEdge * sizeof(unsigned int));
|
|
}
|
|
} else {
|
|
sharpness = 0.0f;
|
|
#ifdef HBRSTITCH
|
|
StitchEdge **stitchEdges = getStitchEdges();
|
|
for (int i = 0; i < mesh->GetStitchCount(); ++i) {
|
|
stitchEdges[i] = 0;
|
|
}
|
|
stitchccw = 1;
|
|
raystitchccw = 1;
|
|
stitchdatavalid = 0;
|
|
#endif
|
|
if (fvarbits) {
|
|
const int fvarcount = mesh->GetFVarCount();
|
|
int fvarbitsSizePerEdge = ((fvarcount + 15) / 16);
|
|
memset(fvarbits, 0xff, fvarbitsSizePerEdge * sizeof(unsigned int));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
HbrHalfedge<T>::~HbrHalfedge() {
|
|
Clear();
|
|
}
|
|
|
|
template <class T>
|
|
void
|
|
HbrHalfedge<T>::Clear() {
|
|
if (opposite) {
|
|
opposite->opposite = 0;
|
|
if (vchild != -1) {
|
|
// Transfer ownership of the vchild to the opposite ptr
|
|
opposite->vchild = vchild;
|
|
|
|
HbrVertex<T> *vchildVert = GetMesh()->GetVertex(vchild);
|
|
// Done this way just for assertion sanity
|
|
vchildVert->SetParent(static_cast<HbrHalfedge*>(0));
|
|
vchildVert->SetParent(opposite);
|
|
vchild = -1;
|
|
}
|
|
opposite = 0;
|
|
}
|
|
// Orphan the child vertex
|
|
else if (vchild != -1) {
|
|
HbrVertex<T> *vchildVert = GetMesh()->GetVertex(vchild);
|
|
vchildVert->SetParent(static_cast<HbrHalfedge*>(0));
|
|
vchild = -1;
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
HbrVertex<T>*
|
|
HbrHalfedge<T>::Subdivide() {
|
|
HbrMesh<T>* mesh = GetMesh();
|
|
if (vchild != -1) return mesh->GetVertex(vchild);
|
|
// Make sure that our opposite doesn't "own" a subdivided vertex
|
|
// already. If it does, use that
|
|
if (opposite && opposite->vchild != -1) return mesh->GetVertex(opposite->vchild);
|
|
HbrVertex<T>* vchildVert = mesh->GetSubdivision()->Subdivide(mesh, this);
|
|
vchild = vchildVert->GetID();
|
|
vchildVert->SetParent(this);
|
|
return vchildVert;
|
|
}
|
|
|
|
template <class T>
|
|
void
|
|
HbrHalfedge<T>::GuaranteeNeighbor() {
|
|
HbrMesh<T>* mesh = GetMesh();
|
|
mesh->GetSubdivision()->GuaranteeNeighbor(mesh, this);
|
|
}
|
|
|
|
// Determines whether an edge is infinitely sharp as far as its
|
|
// facevarying data is concerned. Happens if the faces on both sides
|
|
// disagree on the facevarying data at either of the shared vertices
|
|
// on the edge.
|
|
template <class T>
|
|
bool
|
|
HbrHalfedge<T>::GetFVarInfiniteSharp(int datum) {
|
|
|
|
// Check to see if already initialized
|
|
int intindex = datum >> 4;
|
|
int shift = (datum & 15) << 1;
|
|
unsigned int *fvarinfsharp = getFVarInfSharp();
|
|
unsigned int bits = (fvarinfsharp[intindex] >> shift) & 0x3;
|
|
if (bits != 3) {
|
|
assert (bits != 2);
|
|
return bits ? true : false;
|
|
}
|
|
|
|
// If there is no face varying data it can't be infinitely sharp!
|
|
const int fvarwidth = GetMesh()->GetTotalFVarWidth();
|
|
if (!fvarwidth) {
|
|
bits = ~(0x3 << shift);
|
|
fvarinfsharp[intindex] &= bits;
|
|
if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
|
|
return false;
|
|
}
|
|
|
|
// If either incident face is missing, it's a geometric boundary
|
|
// edge, and also a facevarying boundary edge
|
|
HbrFace<T>* left = GetLeftFace(), *right = GetRightFace();
|
|
if (!left || !right) {
|
|
bits = ~(0x2 << shift);
|
|
fvarinfsharp[intindex] &= bits;
|
|
if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
|
|
return true;
|
|
}
|
|
|
|
// Look for the indices on each face which correspond to the
|
|
// origin and destination vertices of the edge
|
|
int lorg = -1, ldst = -1, rorg = -1, rdst = -1, i, nv;
|
|
HbrHalfedge<T>* e;
|
|
e = left->GetFirstEdge();
|
|
nv = left->GetNumVertices();
|
|
for (i = 0; i < nv; ++i) {
|
|
if (e->GetOrgVertex() == GetOrgVertex()) lorg = i;
|
|
if (e->GetOrgVertex() == GetDestVertex()) ldst = i;
|
|
e = e->GetNext();
|
|
}
|
|
e = right->GetFirstEdge();
|
|
nv = right->GetNumVertices();
|
|
for (i = 0; i < nv; ++i) {
|
|
if (e->GetOrgVertex() == GetOrgVertex()) rorg = i;
|
|
if (e->GetOrgVertex() == GetDestVertex()) rdst = i;
|
|
e = e->GetNext();
|
|
}
|
|
assert(lorg >= 0 && ldst >= 0 && rorg >= 0 && rdst >= 0);
|
|
// Compare the facevarying data to some tolerance
|
|
const int startindex = GetMesh()->GetFVarIndices()[datum];
|
|
const int width = GetMesh()->GetFVarWidths()[datum];
|
|
if (!right->GetFVarData(rorg).Compare(left->GetFVarData(lorg), startindex, width, 0.001f) ||
|
|
!right->GetFVarData(rdst).Compare(left->GetFVarData(ldst), startindex, width, 0.001f)) {
|
|
bits = ~(0x2 << shift);
|
|
fvarinfsharp[intindex] &= bits;
|
|
if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
|
|
return true;
|
|
}
|
|
|
|
bits = ~(0x3 << shift);
|
|
fvarinfsharp[intindex] &= bits;
|
|
if (opposite) opposite->getFVarInfSharp()[intindex] &= bits;
|
|
return false;
|
|
}
|
|
|
|
template <class T>
|
|
bool
|
|
HbrHalfedge<T>::IsFVarInfiniteSharpAnywhere() {
|
|
|
|
if (sharpness > k_Smooth) {
|
|
return true;
|
|
}
|
|
|
|
for (int i = 0; i < GetMesh()->GetFVarCount(); ++i) {
|
|
if (GetFVarInfiniteSharp(i)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <class T>
|
|
float
|
|
HbrHalfedge<T>::GetFVarSharpness(int datum, bool ignoreGeometry) {
|
|
|
|
if (GetFVarInfiniteSharp(datum)) return k_InfinitelySharp;
|
|
|
|
if (!ignoreGeometry) {
|
|
// If it's a geometrically sharp edge it's going to be a
|
|
// facevarying sharp edge too
|
|
if (sharpness > k_Smooth) {
|
|
SetFVarInfiniteSharp(datum, true);
|
|
return k_InfinitelySharp;
|
|
}
|
|
}
|
|
return k_Smooth;
|
|
}
|
|
|
|
|
|
template <class T>
|
|
std::ostream&
|
|
operator<<(std::ostream& out, const HbrHalfedge<T>& edge) {
|
|
if (edge.IsBoundary()) out << "boundary ";
|
|
out << "edge connecting ";
|
|
if (edge.GetOrgVertex())
|
|
out << *edge.GetOrgVertex();
|
|
else
|
|
out << "(none)";
|
|
out << " to ";
|
|
if (edge.GetDestVertex()) {
|
|
out << *edge.GetDestVertex();
|
|
} else {
|
|
out << "(none)";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// Sorts half edges by the relative ordering of the incident faces'
|
|
// paths.
|
|
template <class T>
|
|
class HbrHalfedgeCompare {
|
|
public:
|
|
bool operator() (const HbrHalfedge<T>* a, HbrHalfedge<T>* b) const {
|
|
return (a->GetFace()->GetPath() < b->GetFace()->GetPath());
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
class HbrHalfedgeOperator {
|
|
public:
|
|
virtual void operator() (HbrHalfedge<T> &edge) = 0;
|
|
virtual ~HbrHalfedgeOperator() {}
|
|
};
|
|
|
|
} // end namespace OPENSUBDIV_VERSION
|
|
using namespace OPENSUBDIV_VERSION;
|
|
|
|
} // end namespace OpenSubdiv
|
|
|
|
#endif /* HBRHALFEDGE_H */
|