mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-26 21:40:07 +00:00
Enable GregoryBasis patch.
PatchTablesFactory fills 20 indices topology into patchtable, and use it for eval and draw. note: currently screen-space adaptive tessellation of gregory basis patches is broken and cracks appear around them.
This commit is contained in:
parent
5415bc23b9
commit
24f9dc154b
@ -30,7 +30,7 @@ static float _colors[5][7][4] = {{{1.0f, 1.0f, 1.0f, 1.0f}, // regular
|
||||
{0.0f, 1.0f, 0.0f, 1.0f}, // corner
|
||||
{1.0f, 1.0f, 0.0f, 1.0f}, // gregory
|
||||
{1.0f, 0.5f, 0.0f, 1.0f}, // gregory boundary
|
||||
{1.0f, 1.0f, 0.0f, 1.0f}}, // gregory basis
|
||||
{1.0f, 0.7f, 0.3f, 1.0f}}, // gregory basis
|
||||
|
||||
{{0.0f, 1.0f, 1.0f, 1.0f}, // regular pattern 0
|
||||
{0.0f, 0.5f, 1.0f, 1.0f}, // regular pattern 1
|
||||
|
@ -376,26 +376,50 @@ createOsdMesh(ShapeDesc const & shapeDesc, int level) {
|
||||
Far::StencilTables const * varyingStencils =
|
||||
Far::StencilTablesFactory::Create(*g_topologyRefiner, soptions);
|
||||
|
||||
g_kernelBatches.clear();
|
||||
g_kernelBatches.push_back(Far::StencilTablesFactory::Create(*vertexStencils));
|
||||
|
||||
// Create an Osd Compute context, used to "pose" the vertices with
|
||||
// the stencils tables
|
||||
delete g_computeCtx;
|
||||
g_computeCtx = Osd::CpuComputeContext::Create(vertexStencils, varyingStencils);
|
||||
|
||||
|
||||
// Generate bi-cubic patch tables for the limit surface
|
||||
Far::PatchTablesFactory::Options poptions;
|
||||
// optional : pass the vertex stencils so that the factory can generate gregory basis
|
||||
// stencils (faster evaluation)
|
||||
poptions.adaptiveStencilTables = vertexStencils;
|
||||
poptions.adaptiveVaryingStencilTables = varyingStencils;
|
||||
|
||||
Far::PatchTables const * patchTables =
|
||||
Far::PatchTablesFactory::Create(*g_topologyRefiner, poptions);
|
||||
|
||||
Far::StencilTables const *inStencils[] = {
|
||||
vertexStencils, patchTables->GetEndCapVertexStencilTables()
|
||||
};
|
||||
Far::StencilTables const *concatStencils =
|
||||
Far::StencilTablesFactory::Create(2, inStencils);
|
||||
|
||||
// add gregory basis vertices FIXME:
|
||||
if (patchTables->GetEndCapVertexStencilTables()) {
|
||||
nverts += patchTables->GetEndCapVertexStencilTables()->GetNumStencils();
|
||||
}
|
||||
|
||||
|
||||
Far::StencilTables const *concatVaryingStencils = varyingStencils;
|
||||
if (varyingStencils and patchTables->GetEndCapVaryingStencilTables()) {
|
||||
Far::StencilTables const *inVaryingStencils[] = {
|
||||
varyingStencils, patchTables->GetEndCapVaryingStencilTables()
|
||||
};
|
||||
concatVaryingStencils =
|
||||
Far::StencilTablesFactory::Create(2, inVaryingStencils);
|
||||
}
|
||||
|
||||
// Create an Osd Compute context, used to "pose" the vertices with
|
||||
// the stencils tables
|
||||
delete g_computeCtx;
|
||||
g_computeCtx = Osd::CpuComputeContext::Create(concatStencils, concatVaryingStencils);
|
||||
|
||||
|
||||
// Create a limit Eval context with the patch tables
|
||||
delete g_evalCtx;
|
||||
g_evalCtx = Osd::CpuEvalLimitContext::Create(*patchTables);
|
||||
|
||||
g_kernelBatches.clear();
|
||||
g_kernelBatches.push_back(Far::StencilTablesFactory::Create(*concatStencils));
|
||||
|
||||
}
|
||||
|
||||
{ // Create vertex primvar buffer for the CVs
|
||||
|
@ -56,6 +56,7 @@ static std::vector<ShapeDesc> g_defaultShapes;
|
||||
#include <shapes/catmark_fan.h>
|
||||
#include <shapes/catmark_flap.h>
|
||||
#include <shapes/catmark_flap2.h>
|
||||
#include <shapes/catmark_gregory_test0.h>
|
||||
#include <shapes/catmark_gregory_test1.h>
|
||||
#include <shapes/catmark_gregory_test2.h>
|
||||
#include <shapes/catmark_gregory_test3.h>
|
||||
@ -103,6 +104,7 @@ static void initShapes() {
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_fan", catmark_fan, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_flap", catmark_flap, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_flap2", catmark_flap2, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_gregory_test0", catmark_gregory_test0, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_gregory_test1", catmark_gregory_test1, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_gregory_test2", catmark_gregory_test2, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_gregory_test3", catmark_gregory_test3, kCatmark ) );
|
||||
|
@ -1033,6 +1033,7 @@ createOsdMesh(int level, int kernel) {
|
||||
OpenSubdiv::Osd::MeshBitset bits;
|
||||
bits.set(OpenSubdiv::Osd::MeshAdaptive, doAdaptive);
|
||||
bits.set(OpenSubdiv::Osd::MeshPtexData, true);
|
||||
bits.set(OpenSubdiv::Osd::MeshUseGregoryBasis, true);
|
||||
|
||||
int numVertexElements = g_adaptive ? 3 : 6;
|
||||
int numVaryingElements = 0;
|
||||
|
@ -535,6 +535,7 @@ createOsdMesh(ShapeDesc const & shapeDesc, int level, int kernel, Scheme scheme=
|
||||
bits.set(OpenSubdiv::Osd::MeshUseSingleCreasePatch, doSingleCreasePatch);
|
||||
bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, interleaveVarying);
|
||||
bits.set(OpenSubdiv::Osd::MeshFVarData, g_displayStyle == kFaceVaryingColor);
|
||||
bits.set(OpenSubdiv::Osd::MeshUseGregoryBasis, true);
|
||||
|
||||
int numVertexElements = 3;
|
||||
int numVaryingElements =
|
||||
@ -1157,7 +1158,7 @@ display() {
|
||||
g_mesh->GetDrawContext()->GetPatchArrays();
|
||||
|
||||
// patch drawing
|
||||
int patchCount[12][6][4]; // [Type][Pattern][Rotation] (see far/patchTables.h)
|
||||
int patchCount[13][6][4]; // [Type][Pattern][Rotation] (see far/patchTables.h)
|
||||
int numTotalPatches = 0;
|
||||
int numDrawCalls = 0;
|
||||
memset(patchCount, 0, sizeof(patchCount));
|
||||
@ -1300,6 +1301,8 @@ display() {
|
||||
patchCount[Descriptor::GREGORY][0][0]); y+= 20;
|
||||
g_hud.DrawString(x, y, "Boundary Gregory : %d",
|
||||
patchCount[Descriptor::GREGORY_BOUNDARY][0][0]); y+= 20;
|
||||
g_hud.DrawString(x, y, "Gregory Basis : %d",
|
||||
patchCount[Descriptor::GREGORY_BASIS][0][0]); y+= 20;
|
||||
g_hud.DrawString(x, y, "Trans. Regular : %d %d %d %d %d",
|
||||
patchCount[Descriptor::REGULAR][Descriptor::PATTERN0][0],
|
||||
patchCount[Descriptor::REGULAR][Descriptor::PATTERN1][0],
|
||||
|
@ -60,6 +60,7 @@ static std::vector<ShapeDesc> g_defaultShapes;
|
||||
#include <shapes/catmark_fvar_bound0.h>
|
||||
#include <shapes/catmark_fvar_bound1.h>
|
||||
#include <shapes/catmark_fvar_bound2.h>
|
||||
#include <shapes/catmark_gregory_test0.h>
|
||||
#include <shapes/catmark_gregory_test1.h>
|
||||
#include <shapes/catmark_gregory_test2.h>
|
||||
#include <shapes/catmark_gregory_test3.h>
|
||||
@ -126,6 +127,7 @@ static void initShapes() {
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_fvar_bound0", catmark_fvar_bound0, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_fvar_bound1", catmark_fvar_bound1, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_fvar_bound2", catmark_fvar_bound2, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_gregory_test0", catmark_gregory_test0, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_gregory_test1", catmark_gregory_test1, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_gregory_test2", catmark_gregory_test2, kCatmark ) );
|
||||
g_defaultShapes.push_back( ShapeDesc("catmark_gregory_test3", catmark_gregory_test3, kCatmark ) );
|
||||
|
@ -160,6 +160,7 @@ out block {
|
||||
void emit(int index, vec3 normal)
|
||||
{
|
||||
outpt.v.position = inpt[index].v.position;
|
||||
outpt.v.patchCoord = inpt[index].v.patchCoord;
|
||||
#ifdef SMOOTH_NORMALS
|
||||
outpt.v.normal = inpt[index].v.normal;
|
||||
#else
|
||||
|
@ -103,7 +103,7 @@ vec2 computeUV( int c )
|
||||
|
||||
}
|
||||
|
||||
uniform float scale=0.025;
|
||||
uniform float scale=0.01;
|
||||
|
||||
void main()
|
||||
{
|
||||
@ -116,10 +116,14 @@ void main()
|
||||
|
||||
vec2 ofs = inpt[0].data.xy;
|
||||
|
||||
emit(0, scale * (vec2( 1.0, -2.0)+ofs), uv + dim);
|
||||
emit(1, scale * (vec2( 1.0, 2.0)+ofs), vec2(uv.x+dim.x, uv.y));
|
||||
emit(2, scale * (vec2(-1.0, -2.0)+ofs), vec2(uv.x, uv.y+dim.y));
|
||||
emit(3, scale * (vec2(-1.0, 2.0)+ofs), uv);
|
||||
vec4 clipPos = ProjectionMatrix * inpt[0].position;
|
||||
|
||||
float s = scale * clipPos.w;
|
||||
|
||||
emit(0, s * (vec2( 1.0, -2.0)+ofs), uv + dim);
|
||||
emit(1, s * (vec2( 1.0, 2.0)+ofs), vec2(uv.x+dim.x, uv.y));
|
||||
emit(2, s * (vec2(-1.0, -2.0)+ofs), vec2(uv.x, uv.y+dim.y));
|
||||
emit(3, s * (vec2(-1.0, 2.0)+ofs), uv);
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ static std::vector<ShapeDesc> g_shapes;
|
||||
#include <shapes/catmark_fvar_bound0.h>
|
||||
#include <shapes/catmark_fvar_bound1.h>
|
||||
#include <shapes/catmark_fvar_bound2.h>
|
||||
#include <shapes/catmark_gregory_test0.h>
|
||||
#include <shapes/catmark_gregory_test1.h>
|
||||
#include <shapes/catmark_gregory_test2.h>
|
||||
#include <shapes/catmark_gregory_test3.h>
|
||||
@ -76,6 +77,8 @@ static std::vector<ShapeDesc> g_shapes;
|
||||
#include <shapes/catmark_tent.h>
|
||||
#include <shapes/catmark_torus.h>
|
||||
#include <shapes/catmark_torus_creases0.h>
|
||||
#include <shapes/catmark_smoothtris0.h>
|
||||
#include <shapes/catmark_smoothtris1.h>
|
||||
|
||||
#include <shapes/loop_cube_creases0.h>
|
||||
#include <shapes/loop_cube_creases1.h>
|
||||
@ -113,6 +116,7 @@ static void initShapes() {
|
||||
g_shapes.push_back( ShapeDesc("catmark_fvar_bound0", catmark_fvar_bound0, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_fvar_bound1", catmark_fvar_bound1, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_fvar_bound2", catmark_fvar_bound2, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_gregory_test0", catmark_gregory_test0, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_gregory_test1", catmark_gregory_test1, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_gregory_test2", catmark_gregory_test2, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_gregory_test3", catmark_gregory_test3, kCatmark ) );
|
||||
@ -131,6 +135,8 @@ static void initShapes() {
|
||||
g_shapes.push_back( ShapeDesc("catmark_tent", catmark_tent, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_torus", catmark_torus, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_torus_creases0", catmark_torus_creases0, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_smoothtris0", catmark_smoothtris0, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_smoothtris1", catmark_smoothtris1, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_car", catmark_car, kCatmark ) );
|
||||
g_shapes.push_back( ShapeDesc("catmark_bishop", catmark_bishop, kCatmark ) );
|
||||
|
||||
|
@ -635,23 +635,18 @@ createGregoryBasis(OpenSubdiv::Far::PatchTables const & patchTables,
|
||||
typedef OpenSubdiv::Far::PatchDescriptor PatchDescriptor;
|
||||
|
||||
int npatches = 0;
|
||||
int patchArray = 0;
|
||||
for (int array=0; array<(int)patchTables.GetNumPatchArrays(); ++array) {
|
||||
if (patchTables.GetPatchArrayDescriptor(array).GetType()==
|
||||
PatchDescriptor::GREGORY_BASIS) {
|
||||
npatches = patchTables.GetNumPatches(array);
|
||||
patchArray = array;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int nedges = npatches * 20;
|
||||
std::vector<int> vertsperedge(nedges), edgeindices(nedges*2);
|
||||
std::vector<Vertex> edgeverts(npatches*20);
|
||||
|
||||
OpenSubdiv::Far::StencilTables const * gstencils =
|
||||
patchTables.GetEndCapStencilTables();
|
||||
assert(gstencils);
|
||||
|
||||
gstencils->UpdateValues(&vertexBuffer[0], &edgeverts[0]);
|
||||
|
||||
for (int patch=0; patch<npatches; ++patch) {
|
||||
|
||||
@ -664,33 +659,43 @@ createGregoryBasis(OpenSubdiv::Far::PatchTables const & patchTables,
|
||||
int offset = patch * 20,
|
||||
* vpe = &vertsperedge[offset],
|
||||
* indices = &edgeindices[patch * 40];
|
||||
|
||||
OpenSubdiv::Far::ConstIndexArray const cvs =
|
||||
patchTables.GetPatchVertices(patchArray, patch);
|
||||
|
||||
for (int i=0; i<20; ++i) {
|
||||
vpe[i] = 2;
|
||||
indices[i*2] = basisedges[i*2] + offset;
|
||||
indices[i*2+1] = basisedges[i*2+1] + offset;
|
||||
indices[i*2] = cvs[basisedges[i*2]];
|
||||
indices[i*2+1] = cvs[basisedges[i*2+1]];
|
||||
}
|
||||
|
||||
Vertex const * verts = &edgeverts[offset];
|
||||
//Vertex const * verts = &edgeverts[offset];
|
||||
static char buf[16];
|
||||
for (int i=0; i<4; ++i) {
|
||||
int vid = i * 5;
|
||||
snprintf(buf, 16, " P%d", i);
|
||||
g_font->Print3D(verts[vid].GetPos(), buf, 3);
|
||||
snprintf(buf, 16, " Ep%d", i);
|
||||
g_font->Print3D(verts[vid+1].GetPos(), buf, 3);
|
||||
snprintf(buf, 16, " Em%d", i);
|
||||
g_font->Print3D(verts[vid+2].GetPos(), buf, 3);
|
||||
snprintf(buf, 16, " Fp%d", i);
|
||||
g_font->Print3D(verts[vid+3].GetPos(), buf, 3);
|
||||
snprintf(buf, 16, " Fm%d", i);
|
||||
g_font->Print3D(verts[vid+4].GetPos(), buf, 3);
|
||||
int vid = patch * 20 + i * 5;
|
||||
|
||||
const float *P = vertexBuffer[cvs[i*5+0]].GetPos();
|
||||
const float *Ep = vertexBuffer[cvs[i*5+1]].GetPos();
|
||||
const float *Em = vertexBuffer[cvs[i*5+2]].GetPos();
|
||||
const float *Fp = vertexBuffer[cvs[i*5+3]].GetPos();
|
||||
const float *Fm = vertexBuffer[cvs[i*5+4]].GetPos();
|
||||
|
||||
snprintf(buf, 16, " P%d (%d)", i, vid);
|
||||
g_font->Print3D(P, buf, 3);
|
||||
snprintf(buf, 16, " Ep%d (%d)", i, vid+1);
|
||||
g_font->Print3D(Ep, buf, 3);
|
||||
snprintf(buf, 16, " Em%d (%d)", i, vid+2);
|
||||
g_font->Print3D(Em, buf, 3);
|
||||
snprintf(buf, 16, " Fp%d (%d)", i, vid+3);
|
||||
g_font->Print3D(Fp, buf, 3);
|
||||
snprintf(buf, 16, " Fm%d (%d)", i, vid+4);
|
||||
g_font->Print3D(Fm, buf, 3);
|
||||
}
|
||||
}
|
||||
|
||||
GLMesh::Options options;
|
||||
gregoryWire.Initialize(options, (int)edgeverts.size(), (int)vertsperedge.size(),
|
||||
&vertsperedge[0], &edgeindices[0], (float const * )&edgeverts[0]);
|
||||
|
||||
gregoryWire.Initialize(options, (int)vertexBuffer.size(), (int)vertsperedge.size(),
|
||||
&vertsperedge[0], &edgeindices[0], (float const *)&vertexBuffer[0]);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -770,17 +775,6 @@ createVtrMesh(Shape * shape, int maxlevel) {
|
||||
// Stencils
|
||||
//
|
||||
|
||||
// create vertex primvar data buffer
|
||||
std::vector<Vertex> vertexBuffer(refiner->GetNumVerticesTotal());
|
||||
Vertex * verts = &vertexBuffer[0];
|
||||
|
||||
// copy coarse vertices positions
|
||||
int ncoarseverts = shape->GetNumVertices();
|
||||
for (int i=0; i<ncoarseverts; ++i) {
|
||||
float * ptr = &shape->verts[i*3];
|
||||
verts[i].SetPosition(ptr[0], ptr[1], ptr[2]);
|
||||
}
|
||||
|
||||
//#define no_stencils
|
||||
#ifdef no_stencils
|
||||
{
|
||||
@ -800,8 +794,6 @@ createVtrMesh(Shape * shape, int maxlevel) {
|
||||
|
||||
stencilTables =
|
||||
OpenSubdiv::Far::StencilTablesFactory::Create(*refiner, options);
|
||||
|
||||
stencilTables->UpdateValues(verts, verts + ncoarseverts);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -848,6 +840,39 @@ createVtrMesh(Shape * shape, int maxlevel) {
|
||||
refiner->InterpolateFaceVarying(values, values + nCoarseValues);
|
||||
}
|
||||
}
|
||||
// note: gregoryBasisStencilTables is owned by patchTables.
|
||||
OpenSubdiv::Far::StencilTables const * gregoryBasisStencilTables =
|
||||
patchTables->GetEndCapVertexStencilTables();
|
||||
|
||||
if (gregoryBasisStencilTables) {
|
||||
OpenSubdiv::Far::StencilTables const *inStencilTables[] = {
|
||||
stencilTables, gregoryBasisStencilTables
|
||||
};
|
||||
OpenSubdiv::Far::StencilTables const *concatStencilTables =
|
||||
concatStencilTables = OpenSubdiv::Far::StencilTablesFactory::Create(
|
||||
2, inStencilTables);
|
||||
delete stencilTables;
|
||||
stencilTables = concatStencilTables;
|
||||
}
|
||||
|
||||
int numTotalVerts = shape->GetNumVertices() + stencilTables->GetNumStencils();
|
||||
|
||||
// create vertex primvar data buffer
|
||||
std::vector<Vertex> vertexBuffer(numTotalVerts);
|
||||
Vertex * verts = &vertexBuffer[0];
|
||||
|
||||
// copy coarse vertices positions
|
||||
int ncoarseverts = shape->GetNumVertices();
|
||||
for (int i=0; i<ncoarseverts; ++i) {
|
||||
float * ptr = &shape->verts[i*3];
|
||||
verts[i].SetPosition(ptr[0], ptr[1], ptr[2]);
|
||||
}
|
||||
|
||||
//
|
||||
// apply stencils
|
||||
//
|
||||
stencilTables->UpdateValues(verts, verts + ncoarseverts);
|
||||
|
||||
s.Stop();
|
||||
|
||||
//
|
||||
@ -1391,7 +1416,7 @@ initHUD() {
|
||||
g_hud.AddCheckBox("Adaptive (`)", g_Adaptive!=0, 10, 350, callbackAdaptive, 0, '`');
|
||||
|
||||
|
||||
g_hud.AddSlider("Font Scale", 0.0f, 0.1f, 0.025f,
|
||||
g_hud.AddSlider("Font Scale", 0.0f, 0.1f, 0.01f,
|
||||
-900, -50, 100, false, callbackScale, 0);
|
||||
|
||||
for (int i = 1; i < 11; ++i) {
|
||||
|
@ -89,7 +89,8 @@ getQuadOffsets(Vtr::Level const & level, Vtr::Index fIndex,
|
||||
16 + maxvalence - 3
|
||||
|
||||
// limit valence of 30 because we use a pre-computed closed-form 'ef' table
|
||||
static const int MAX_VALENCE=30,
|
||||
// XXXtakahito: revisit here to determine appropriate size
|
||||
static const int MAX_VALENCE=(30*2),
|
||||
MAX_ELEMS = GetNumMaxElems(MAX_VALENCE);
|
||||
|
||||
namespace Far {
|
||||
@ -271,6 +272,9 @@ struct ProtoBasis {
|
||||
//
|
||||
|
||||
Point P[4], Ep[4], Em[4], Fp[4], Fm[4];
|
||||
|
||||
// for varying interpolation
|
||||
Point V[4];
|
||||
};
|
||||
|
||||
int
|
||||
@ -293,6 +297,7 @@ ProtoBasis::OffsetIndices(Index offset) {
|
||||
Em[vid].OffsetIndices(offset);
|
||||
Fp[vid].OffsetIndices(offset);
|
||||
Fm[vid].OffsetIndices(offset);
|
||||
V[vid].OffsetIndices(offset);
|
||||
}
|
||||
}
|
||||
void
|
||||
@ -349,6 +354,8 @@ ProtoBasis::ProtoBasis(Vtr::Level const & level, Index faceIndex, int fvarChanne
|
||||
for (int vid=0; vid<4; ++vid) {
|
||||
|
||||
org[vid] = facePoints[vid];
|
||||
// save for varying stencils
|
||||
V[vid] = facePoints[vid];
|
||||
|
||||
int ringSize =
|
||||
level.gatherQuadRegularRingAroundVertex(
|
||||
@ -595,6 +602,8 @@ GregoryBasisFactory::Create(TopologyRefiner const & refiner,
|
||||
|
||||
basis.Copy(result->_sizes, &result->_indices[0], &result->_weights[0]);
|
||||
|
||||
// note: this function doesn't create varying stencils.
|
||||
|
||||
for (int i=0, offset=0; i<20; ++i) {
|
||||
result->_offsets[i] = offset;
|
||||
offset += result->_sizes[i];
|
||||
@ -607,15 +616,19 @@ GregoryBasisFactory::Create(TopologyRefiner const & refiner,
|
||||
// GregoryBasisFactory for Vertex StencilTables
|
||||
//
|
||||
GregoryBasisFactory::GregoryBasisFactory(TopologyRefiner const & refiner,
|
||||
StencilTables const & stencils,
|
||||
StencilTables const *stencils,
|
||||
StencilTables const *varyingStencils,
|
||||
int numpatches, int maxvalence) :
|
||||
_currentStencil(0), _refiner(refiner),
|
||||
_stencils(stencils), _alloc(GetNumMaxElems(maxvalence)) {
|
||||
_stencils(stencils), _varyingStencils(varyingStencils),
|
||||
_alloc(GetNumMaxElems(maxvalence)),
|
||||
_varyingAlloc(GetNumMaxElems(maxvalence)) {
|
||||
|
||||
// Sanity check: the mesh must be adaptively refined
|
||||
assert(not _refiner.IsUniform());
|
||||
|
||||
_alloc.Resize(numpatches * 20);
|
||||
_varyingAlloc.Resize(numpatches * 20);
|
||||
|
||||
// Gregory limit stencils have indices that are relative to the level
|
||||
// (maxlevel) of subdivision. These indices need to be offset to match
|
||||
@ -624,9 +637,10 @@ GregoryBasisFactory::GregoryBasisFactory(TopologyRefiner const & refiner,
|
||||
// (single weight of 1.0f) as place-holders for coarse mesh vertices,
|
||||
// which also needs to be accounted for.
|
||||
_stencilsOffset=-1;
|
||||
{ int maxlevel = _refiner.GetMaxLevel(),
|
||||
{
|
||||
int maxlevel = _refiner.GetMaxLevel(),
|
||||
nverts = _refiner.GetNumVerticesTotal(),
|
||||
nstencils = _stencils.GetNumStencils();
|
||||
nstencils = _stencils->GetNumStencils();
|
||||
if (nstencils==nverts) {
|
||||
|
||||
// the table contain stencils for the control vertices
|
||||
@ -645,17 +659,20 @@ GregoryBasisFactory::GregoryBasisFactory(TopologyRefiner const & refiner,
|
||||
}
|
||||
}
|
||||
static inline void
|
||||
factorizeBasisVertex(StencilTables const & stencils, Point const & p, ProtoStencil dst) {
|
||||
factorizeBasisVertex(StencilTables const * stencils, Point const & p, ProtoStencil dst) {
|
||||
// Use the Allocator to factorize the Gregory patch influence CVs with the
|
||||
// supporting CVs from the stencil tables.
|
||||
if (!stencils) return;
|
||||
|
||||
dst.Clear();
|
||||
for (int j=0; j<p.GetSize(); ++j) {
|
||||
dst.AddWithWeight(stencils,
|
||||
dst.AddWithWeight(*stencils,
|
||||
p.GetIndices()[j], p.GetWeights()[j]);
|
||||
}
|
||||
}
|
||||
bool
|
||||
GregoryBasisFactory::AddPatchBasis(Index faceIndex) {
|
||||
GregoryBasisFactory::AddPatchBasis(Index faceIndex,
|
||||
bool verticesMask[4][5]) {
|
||||
|
||||
// Gregory patches only exist on the hight
|
||||
Vtr::Level const & level = _refiner.getLevel(_refiner.GetMaxLevel());
|
||||
@ -677,21 +694,41 @@ GregoryBasisFactory::AddPatchBasis(Index faceIndex) {
|
||||
// expressed as a linear combination of vertices from the coarse control
|
||||
// mesh with no data dependencies
|
||||
for (int i=0; i<4; ++i) {
|
||||
int offset = _currentStencil + i * 5;
|
||||
factorizeBasisVertex(_stencils, basis.P[i], _alloc[offset]);
|
||||
factorizeBasisVertex(_stencils, basis.Ep[i], _alloc[offset+1]);
|
||||
factorizeBasisVertex(_stencils, basis.Em[i], _alloc[offset+2]);
|
||||
factorizeBasisVertex(_stencils, basis.Fp[i], _alloc[offset+3]);
|
||||
factorizeBasisVertex(_stencils, basis.Fm[i], _alloc[offset+4]);
|
||||
if (verticesMask[i][0]) {
|
||||
factorizeBasisVertex(_stencils, basis.P[i], _alloc[_currentStencil]);
|
||||
// need varying stencils only for corners
|
||||
factorizeBasisVertex(_varyingStencils, basis.V[i], _varyingAlloc[_currentStencil]);
|
||||
++_currentStencil;
|
||||
}
|
||||
if (verticesMask[i][1]) {
|
||||
factorizeBasisVertex(_stencils, basis.Ep[i], _alloc[_currentStencil]);
|
||||
factorizeBasisVertex(_varyingStencils, basis.V[i], _varyingAlloc[_currentStencil]);
|
||||
++_currentStencil;
|
||||
}
|
||||
if (verticesMask[i][2]) {
|
||||
factorizeBasisVertex(_stencils, basis.Em[i], _alloc[_currentStencil]);
|
||||
factorizeBasisVertex(_varyingStencils, basis.V[i], _varyingAlloc[_currentStencil]);
|
||||
++_currentStencil;
|
||||
}
|
||||
if (verticesMask[i][3]) {
|
||||
factorizeBasisVertex(_stencils, basis.Fp[i], _alloc[_currentStencil]);
|
||||
factorizeBasisVertex(_varyingStencils, basis.V[i], _varyingAlloc[_currentStencil]);
|
||||
++_currentStencil;
|
||||
}
|
||||
if (verticesMask[i][4]) {
|
||||
factorizeBasisVertex(_stencils, basis.Fm[i], _alloc[_currentStencil]);
|
||||
factorizeBasisVertex(_varyingStencils, basis.V[i], _varyingAlloc[_currentStencil]);
|
||||
++_currentStencil;
|
||||
}
|
||||
}
|
||||
_currentStencil += 20;
|
||||
return true;
|
||||
}
|
||||
StencilTables const *
|
||||
GregoryBasisFactory::CreateStencilTables(int const permute[20]) {
|
||||
GregoryBasisFactory::createStencilTables(int const permute[20],
|
||||
StencilAllocator &alloc) {
|
||||
|
||||
int nstencils = (int)_alloc.GetNumStencils(),
|
||||
nelems = _alloc.GetNumVerticesTotal();
|
||||
int nstencils = _currentStencil;
|
||||
int nelems = alloc.GetNumVerticesTotal();
|
||||
|
||||
if (nstencils==0 or nelems==0) {
|
||||
return 0;
|
||||
@ -716,7 +753,7 @@ GregoryBasisFactory::CreateStencilTables(int const permute[20]) {
|
||||
index = baseIndex + permute[localIndex];
|
||||
}
|
||||
|
||||
*dst._size = _alloc.CopyStencil(index, dst._indices, dst._weights);
|
||||
*dst._size = alloc.CopyStencil(index, dst._indices, dst._weights);
|
||||
|
||||
dst.Next();
|
||||
}
|
||||
|
@ -116,25 +116,39 @@ public:
|
||||
// this factory is active.
|
||||
//
|
||||
GregoryBasisFactory(TopologyRefiner const & refiner,
|
||||
StencilTables const & stencils,
|
||||
StencilTables const * stencils,
|
||||
StencilTables const * varyingStencils,
|
||||
int numpatches, int maxvalence);
|
||||
|
||||
// Creates a basis for the face and adds it to the stencil pool allocator
|
||||
bool AddPatchBasis(Index faceIndex);
|
||||
// Creates a basis for the vertices specified in mask on the face and
|
||||
// adds it to the stencil pool allocator
|
||||
bool AddPatchBasis(Index faceIndex,
|
||||
bool newVerticesMask[4][5]);
|
||||
|
||||
// After all the patches have been collected, create the final table
|
||||
StencilTables const * CreateStencilTables(int const permute[20]=0);
|
||||
StencilTables const * CreateVertexStencilTables(int const permute[20]=0) {
|
||||
return createStencilTables(permute, _alloc);
|
||||
}
|
||||
StencilTables const * CreateVaryingStencilTables(int const permute[20]=0) {
|
||||
return createStencilTables(permute, _varyingAlloc);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
StencilTables const * createStencilTables(int const permute[20],
|
||||
StencilAllocator &alloc);
|
||||
// XXX: StencilAllocator method's constness needs to be fixed.
|
||||
|
||||
int _currentStencil;
|
||||
|
||||
TopologyRefiner const & _refiner; // XXXX these should be smart pointers !
|
||||
|
||||
Index _stencilsOffset;
|
||||
|
||||
StencilTables const & _stencils;
|
||||
StencilTables const * _stencils;
|
||||
StencilTables const * _varyingStencils;
|
||||
StencilAllocator _alloc;
|
||||
StencilAllocator _varyingAlloc;
|
||||
};
|
||||
|
||||
} // end namespace Far
|
||||
|
@ -33,7 +33,9 @@ namespace OPENSUBDIV_VERSION {
|
||||
namespace Far {
|
||||
|
||||
PatchTables::PatchTables(int maxvalence) :
|
||||
_maxValence(maxvalence), _endcapStencilTables(0) { }
|
||||
_maxValence(maxvalence),
|
||||
_endcapVertexStencilTables(0),
|
||||
_endcapVaryingStencilTables(0) { }
|
||||
|
||||
// Copy constructor
|
||||
// XXXX manuelk we need to eliminate this constructor (C++11 smart pointers)
|
||||
@ -43,21 +45,21 @@ PatchTables::PatchTables(PatchTables const & src) :
|
||||
_patchArrays(src._patchArrays),
|
||||
_patchVerts(src._patchVerts),
|
||||
_paramTable(src._paramTable),
|
||||
#ifdef ENDCAP_TOPOPOLGY
|
||||
_endcapTopology(src._endcapTopology),
|
||||
#endif
|
||||
_quadOffsetsTable(src._quadOffsetsTable),
|
||||
_vertexValenceTable(src._vertexValenceTable),
|
||||
_fvarChannels(src._fvarChannels),
|
||||
_sharpnessIndices(src._sharpnessIndices),
|
||||
_sharpnessValues(src._sharpnessValues) {
|
||||
|
||||
_endcapStencilTables = src._endcapStencilTables ?
|
||||
new StencilTables(*src._endcapStencilTables) : 0;
|
||||
_endcapVertexStencilTables = src._endcapVertexStencilTables ?
|
||||
new StencilTables(*src._endcapVertexStencilTables) : 0;
|
||||
_endcapVaryingStencilTables = src._endcapVaryingStencilTables ?
|
||||
new StencilTables(*src._endcapVaryingStencilTables) : 0;
|
||||
}
|
||||
|
||||
PatchTables::~PatchTables() {
|
||||
delete _endcapStencilTables;
|
||||
delete _endcapVertexStencilTables;
|
||||
delete _endcapVaryingStencilTables;
|
||||
}
|
||||
|
||||
//
|
||||
@ -242,13 +244,7 @@ PatchTables::setBicubicFVarPatchChannelValues(int channel, int patchSize,
|
||||
|
||||
inline int
|
||||
getPatchSize(PatchDescriptor desc) {
|
||||
int size = desc.GetNumControlVertices();
|
||||
// XXXX manuelk we do not store the topology for Gregory Basis
|
||||
// patch types yet - so point to the 4 corners of the 0-ring
|
||||
if (desc.GetType() == PatchDescriptor::GREGORY_BASIS) {
|
||||
size = 4;
|
||||
}
|
||||
return size;
|
||||
return desc.GetNumControlVertices();
|
||||
}
|
||||
|
||||
void
|
||||
@ -339,13 +335,7 @@ PatchTables::GetPatchArrayVertices(int arrayIndex) const {
|
||||
ConstIndexArray
|
||||
PatchTables::GetPatchVertices(PatchHandle const & handle) const {
|
||||
PatchArray const & pa = getPatchArray(handle.arrayIndex);
|
||||
|
||||
Index vert = pa.vertIndex;
|
||||
// XXXX manuelk we do not store the topology for Gregory Basis
|
||||
// patch types yet - so point to the 4 corners of the 0-ring
|
||||
vert += (pa.desc.GetType() == PatchDescriptor::GREGORY_BASIS) ?
|
||||
handle.vertIndex / 5 : handle.vertIndex;
|
||||
assert(vert<(Index)_patchVerts.size());
|
||||
Index vert = pa.vertIndex + handle.vertIndex;
|
||||
return ConstIndexArray(&_patchVerts[vert], getPatchSize(pa.desc));
|
||||
}
|
||||
ConstIndexArray
|
||||
@ -409,7 +399,7 @@ bool
|
||||
PatchTables::IsFeatureAdaptive() const {
|
||||
|
||||
// check for presence of tables only used by adaptive patches
|
||||
if (not _vertexValenceTable.empty() or _endcapStencilTables)
|
||||
if (not _vertexValenceTable.empty() or _endcapVertexStencilTables)
|
||||
return true;
|
||||
|
||||
// otherwise, we have to check each patch array
|
||||
|
@ -174,11 +174,9 @@ public:
|
||||
}
|
||||
|
||||
/// \brief Returns a stencil table for the control vertices of end-cap patches
|
||||
StencilTables const * GetEndCapStencilTables() const { return _endcapStencilTables; }
|
||||
StencilTables const * GetEndCapVertexStencilTables() const { return _endcapVertexStencilTables; }
|
||||
StencilTables const * GetEndCapVaryingStencilTables() const { return _endcapVaryingStencilTables; }
|
||||
|
||||
Index GetEndCapStencilIndex(PatchHandle const & handle) const {
|
||||
return handle.vertIndex;
|
||||
}
|
||||
//@}
|
||||
|
||||
|
||||
@ -418,10 +416,8 @@ private:
|
||||
|
||||
// XXXX manuelk end-cap stencils will obsolete the other tables
|
||||
|
||||
StencilTables const * _endcapStencilTables;
|
||||
#ifdef ENDCAP_TOPOPOLGY
|
||||
std::vector<Index> _endcapTopology;
|
||||
#endif
|
||||
StencilTables const * _endcapVertexStencilTables;
|
||||
StencilTables const * _endcapVaryingStencilTables;
|
||||
QuadOffsetsTable _quadOffsetsTable; // Quad offsets (for Gregory patches)
|
||||
VertexValenceTable _vertexValenceTable; // Vertex valence table (for Gregory patches)
|
||||
|
||||
@ -494,10 +490,10 @@ PatchTables::Evaluate(PatchHandle const & handle, float s, float t,
|
||||
}
|
||||
} else if (ptype==PatchDescriptor::GREGORY_BASIS) {
|
||||
|
||||
assert(_endcapStencilTables);
|
||||
assert(_endcapVertexStencilTables);
|
||||
|
||||
GetBezierWeights(bits, s, t, Q, Qd1, Qd2);
|
||||
InterpolateGregoryPatch(_endcapStencilTables, handle.vertIndex,
|
||||
InterpolateGregoryPatch(_endcapVertexStencilTables, handle.vertIndex,
|
||||
s, t, Q, Qd1, Qd2, src, dst);
|
||||
|
||||
} else if (ptype==PatchDescriptor::QUADS) {
|
||||
|
@ -904,7 +904,6 @@ PatchTablesFactory::computePatchParam(TopologyRefiner const & refiner,
|
||||
return ++coord;
|
||||
}
|
||||
|
||||
#ifdef ENDCAP_TOPOPOLGY
|
||||
// XXXX manuelk work in progress for end-cap topology gathering
|
||||
//
|
||||
// Populates the topology table used by Gregory-basis patches
|
||||
@ -913,17 +912,21 @@ PatchTablesFactory::computePatchParam(TopologyRefiner const & refiner,
|
||||
// Note 2: this code attempts to identify basis vertices shared along
|
||||
// gregory patch edges
|
||||
static int
|
||||
gatherGregoryBasisTopology(Vtr::Level const& level, Index faceIndex, int numVertices,
|
||||
gatherGregoryBasisTopology(Vtr::Level const& level, Index faceIndex,
|
||||
int numGregoryBasisPatches,
|
||||
int numGregoryBasisVertices,
|
||||
PatchFaceTag const * levelPatchTags,
|
||||
bool skip[0], std::vector<Index> & basisIndices, PatchTables::PTable & topology) {
|
||||
std::vector<Index> & basisIndices,
|
||||
PatchTables::PatchVertsTable & topology,
|
||||
bool newVerticesMask[4][5]) {
|
||||
|
||||
assert(not topology.empty());
|
||||
Index * dest = &topology[basisIndices.size()*20];
|
||||
Index * dest = &topology[numGregoryBasisPatches*20];
|
||||
|
||||
assert(Vtr::INDEX_INVALID==0xFFFFFFFF);
|
||||
memset(dest, 0xFF, 20*sizeof(Index));
|
||||
|
||||
IndexArray fedges = level.getFaceEdges(faceIndex);
|
||||
ConstIndexArray fedges = level.getFaceEdges(faceIndex);
|
||||
assert(fedges.size()==4);
|
||||
|
||||
for (int i=0; i<4; ++i) {
|
||||
@ -931,7 +934,7 @@ gatherGregoryBasisTopology(Vtr::Level const& level, Index faceIndex, int numVert
|
||||
adjface = 0;
|
||||
|
||||
{ // Gather adjacent faces
|
||||
IndexArray adjfaces = level.getEdgeFaces(edge);
|
||||
ConstIndexArray adjfaces = level.getEdgeFaces(edge);
|
||||
for (int i=0; i<adjfaces.size(); ++i) {
|
||||
if (adjfaces[i]==faceIndex) {
|
||||
// XXXX manuelk if 'edge' is non-manifold, arbitrarily pick the
|
||||
@ -943,12 +946,12 @@ gatherGregoryBasisTopology(Vtr::Level const& level, Index faceIndex, int numVert
|
||||
}
|
||||
// We are looking for adjacent faces that:
|
||||
// - exist (no boundary)
|
||||
// - have alraedy been processed (known CV indices)
|
||||
// - have already been processed (known CV indices)
|
||||
// - are also Gregory basis patches
|
||||
if (adjface!=Vtr::INDEX_INVALID and (adjface < faceIndex) and
|
||||
(not levelPatchTags[adjface]._isRegular)) {
|
||||
|
||||
IndexArray aedges = level.getFaceEdges(adjface);
|
||||
ConstIndexArray aedges = level.getFaceEdges(adjface);
|
||||
int aedge = aedges.FindIndexIn4Tuple(edge);
|
||||
assert(aedge!=Vtr::INDEX_INVALID);
|
||||
|
||||
@ -963,6 +966,11 @@ gatherGregoryBasisTopology(Vtr::Level const& level, Index faceIndex, int numVert
|
||||
basisIndices.size(), sizeof(Index), compare::op);
|
||||
|
||||
int srcBasisIdx = ptr - &basisIndices[0];
|
||||
|
||||
if (!ptr) {
|
||||
// if the adjface is hole, it won't be found
|
||||
break;
|
||||
}
|
||||
assert(ptr and srcBasisIdx>=0 and srcBasisIdx<(int)basisIndices.size());
|
||||
|
||||
// Copy the indices of CVs from the face on the other side of the
|
||||
@ -973,23 +981,26 @@ gatherGregoryBasisTopology(Vtr::Level const& level, Index faceIndex, int numVert
|
||||
{15, 16, 2, 0} };
|
||||
Index * src = &topology[srcBasisIdx*20];
|
||||
for (int j=0; j<4; ++j) {
|
||||
dest[i*4+j] = src[gregoryEdgeVerts[aedge][j]];
|
||||
// invert direction
|
||||
dest[gregoryEdgeVerts[i][3-j]] = src[gregoryEdgeVerts[aedge][j]];
|
||||
}
|
||||
|
||||
skip[i] = true;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 5; ++j) {
|
||||
if (dest[i*5+j]==Vtr::INDEX_INVALID) {
|
||||
// assign new vertex
|
||||
dest[i*5+j] = numGregoryBasisVertices++;
|
||||
newVerticesMask[i][j] = true;
|
||||
} else {
|
||||
skip[i] = false;
|
||||
// share vertex
|
||||
newVerticesMask[i][j] = false;
|
||||
}
|
||||
}
|
||||
for (int i=0; i<20; ++i) {
|
||||
if (dest[i]==Vtr::INDEX_INVALID) {
|
||||
dest[i] = numVertices++;
|
||||
}
|
||||
}
|
||||
basisIndices.push_back(faceIndex);
|
||||
return numVertices;
|
||||
return numGregoryBasisVertices;
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Indexing sharpnesses
|
||||
@ -1602,27 +1613,37 @@ PatchTablesFactory::populateAdaptivePatches(AdaptiveContext & context) {
|
||||
bool hasGregoryPatches =
|
||||
context.RequiresLegacyGregoryPatches() or context.RequiresGregoryBasisPatches();
|
||||
GregoryBasisFactory * gregoryStencilsFactory = 0;
|
||||
#ifdef ENDCAP_TOPOPOLGY
|
||||
int numGregoryBasisVertices=0;
|
||||
int numGregoryBasisPatches=0;
|
||||
std::vector<Index> gregoryBasisIndices;
|
||||
#endif
|
||||
std::vector<Index> endcapTopology;
|
||||
if (hasGregoryPatches) {
|
||||
|
||||
StencilTables const * adaptiveStencils = context.options.adaptiveStencilTables;
|
||||
if (adaptiveStencils and context.RequiresGregoryBasisPatches()) {
|
||||
StencilTables const * adaptiveVertexStencils =
|
||||
context.options.adaptiveStencilTables;
|
||||
StencilTables const * adaptiveVaryingStencils =
|
||||
context.options.adaptiveVaryingStencilTables;
|
||||
|
||||
if (adaptiveVertexStencils and
|
||||
context.RequiresGregoryBasisPatches()) {
|
||||
|
||||
assert(not context.RequiresLegacyGregoryPatches());
|
||||
|
||||
int maxvalence = refiner.GetMaxValence(),
|
||||
npatches = context.patchInventory.GP;
|
||||
|
||||
gregoryStencilsFactory =
|
||||
new GregoryBasisFactory(refiner, *adaptiveStencils, npatches, maxvalence);
|
||||
|
||||
#ifdef ENDCAP_TOPOPOLGY
|
||||
gregoryStencilsFactory =
|
||||
new GregoryBasisFactory(refiner,
|
||||
adaptiveVertexStencils,
|
||||
adaptiveVaryingStencils,
|
||||
npatches, maxvalence);
|
||||
|
||||
// XXX reconsider these variable names:
|
||||
// gregoryBasisIndices holds patch indices,
|
||||
// endcapTopology contains vert indices.
|
||||
gregoryBasisIndices.reserve(npatches);
|
||||
tables->_endcapTopology.resize(npatches*20);
|
||||
#endif
|
||||
endcapTopology.resize(npatches*20);
|
||||
}
|
||||
gregoryVertexFlags.resize(refiner.GetNumVerticesTotal(), false);
|
||||
}
|
||||
@ -1739,19 +1760,24 @@ PatchTablesFactory::populateAdaptivePatches(AdaptiveContext & context) {
|
||||
// Gregory basis end-cap (20 CVs - no quad-offsets / valence tables)
|
||||
assert(i==refiner.GetMaxLevel());
|
||||
// Gregory Boundary Patch (4 CVs 0-ring for varying interpolation)
|
||||
Vtr::ConstIndexArray faceVerts = level->getFaceVertices(faceIndex);
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
iptrs.GP[j] = faceVerts[j] + levelVertOffset;
|
||||
gregoryVertexFlags[iptrs.GP[j]] = true;
|
||||
}
|
||||
iptrs.GP += 4;
|
||||
bool newVerticesMask[4][5];
|
||||
numGregoryBasisVertices = gatherGregoryBasisTopology(*level, faceIndex,
|
||||
numGregoryBasisPatches,
|
||||
numGregoryBasisVertices,
|
||||
levelPatchTags,
|
||||
gregoryBasisIndices,
|
||||
endcapTopology,
|
||||
newVerticesMask);
|
||||
gregoryStencilsFactory->AddPatchBasis(faceIndex, newVerticesMask);
|
||||
|
||||
#ifdef ENDCAP_TOPOPOLGY
|
||||
bool edgeSkip[4];
|
||||
numGregoryBasisVertices = gatherGregoryBasisTopology(*level, faceIndex, numGregoryBasisVertices,
|
||||
levelPatchTags, edgeSkip, gregoryBasisIndices, tables->_endcapTopology);
|
||||
#endif
|
||||
gregoryStencilsFactory->AddPatchBasis(faceIndex);
|
||||
// populate 20 indices with offset
|
||||
for (int j = 0; j < 20; ++j) {
|
||||
iptrs.GP[j] = endcapTopology[numGregoryBasisPatches*20+j]
|
||||
+ refiner.GetNumVerticesTotal();
|
||||
}
|
||||
iptrs.GP += 20;
|
||||
|
||||
++numGregoryBasisPatches;
|
||||
|
||||
pptrs.GP = computePatchParam(refiner, i, faceIndex, 0, pptrs.GP);
|
||||
|
||||
@ -1807,8 +1833,10 @@ PatchTablesFactory::populateAdaptivePatches(AdaptiveContext & context) {
|
||||
}
|
||||
|
||||
if (gregoryStencilsFactory) {
|
||||
tables->_endcapStencilTables =
|
||||
gregoryStencilsFactory->CreateStencilTables();
|
||||
tables->_endcapVertexStencilTables =
|
||||
gregoryStencilsFactory->CreateVertexStencilTables();
|
||||
tables->_endcapVaryingStencilTables =
|
||||
gregoryStencilsFactory->CreateVaryingStencilTables();
|
||||
delete gregoryStencilsFactory;
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,9 @@ public:
|
||||
useFVarQuadEndCaps(true), // XXXX change to false when FVar Gregory is ready
|
||||
numFVarChannels(-1),
|
||||
fvarChannelIndices(0),
|
||||
adaptiveStencilTables(0) { }
|
||||
adaptiveStencilTables(0),
|
||||
adaptiveVaryingStencilTables(0)
|
||||
{ }
|
||||
|
||||
unsigned int generateAllLevels : 1, ///< Include levels from 'firstLevel' to 'maxLevel' (Uniform mode only)
|
||||
triangulateQuads : 1, ///< Triangulate 'QUADS' primitives (Uniform mode only)
|
||||
@ -79,6 +81,7 @@ public:
|
||||
StencilTables const * adaptiveStencilTables; ///< Passing a valid stencil table allows the factory to generate
|
||||
///< stencils for gregory patches and replace them with a much
|
||||
///< more efficient basis.
|
||||
StencilTables const * adaptiveVaryingStencilTables;
|
||||
};
|
||||
|
||||
/// \brief Factory constructor for PatchTables
|
||||
|
@ -190,40 +190,53 @@ StencilTablesFactory::Create(TopologyRefiner const & refiner,
|
||||
StencilTables const *
|
||||
StencilTablesFactory::Create(int numTables, StencilTables const ** tables) {
|
||||
|
||||
StencilTables * result = new StencilTables;
|
||||
// XXXtakahito:
|
||||
// This function returns NULL for empty inputs or erroneous condition.
|
||||
// It's convenient for skipping varying stencils etc, however,
|
||||
// other Create() API returns an empty stencil instead of NULL.
|
||||
// They need to be consistent.
|
||||
|
||||
if ( (numTables<=0) or (not tables)) {
|
||||
return result;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ncvs = tables[0]->GetNumControlVertices(),
|
||||
int ncvs = -1,
|
||||
nstencils = 0,
|
||||
nelems = 0;
|
||||
|
||||
for (int i=0; i<numTables; ++i) {
|
||||
|
||||
StencilTables const & st = *tables[i];
|
||||
StencilTables const * st = tables[i];
|
||||
// allow the tables could have a null entry.
|
||||
if (!st) continue;
|
||||
|
||||
if (st.GetNumControlVertices()!=ncvs) {
|
||||
return result;
|
||||
if (ncvs >= 0 and st->GetNumControlVertices() != ncvs) {
|
||||
return NULL;
|
||||
}
|
||||
nstencils += st.GetNumStencils();
|
||||
nelems += (int)st.GetControlIndices().size();
|
||||
ncvs = st->GetNumControlVertices();
|
||||
nstencils += st->GetNumStencils();
|
||||
nelems += (int)st->GetControlIndices().size();
|
||||
}
|
||||
|
||||
if (ncvs == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StencilTables * result = new StencilTables;
|
||||
result->resize(nstencils, nelems);
|
||||
|
||||
unsigned char * sizes = &result->_sizes[0];
|
||||
Index * indices = &result->_indices[0];
|
||||
float * weights = &result->_weights[0];
|
||||
for (int i=0; i<numTables; ++i) {
|
||||
StencilTables const & st = *tables[i];
|
||||
StencilTables const * st = tables[i];
|
||||
if (!st) continue;
|
||||
|
||||
int st_nstencils = st.GetNumStencils(),
|
||||
st_nelems = (int)st._indices.size();
|
||||
memcpy(sizes, &st._sizes[0], st_nstencils*sizeof(unsigned char));
|
||||
memcpy(indices, &st._indices[0], st_nelems*sizeof(Index));
|
||||
memcpy(weights, &st._weights[0], st_nelems*sizeof(float));
|
||||
int st_nstencils = st->GetNumStencils(),
|
||||
st_nelems = (int)st->_indices.size();
|
||||
memcpy(sizes, &st->_sizes[0], st_nstencils*sizeof(unsigned char));
|
||||
memcpy(indices, &st->_indices[0], st_nelems*sizeof(Index));
|
||||
memcpy(weights, &st->_weights[0], st_nelems*sizeof(float));
|
||||
|
||||
sizes += st_nstencils;
|
||||
indices += st_nelems;
|
||||
|
@ -200,6 +200,7 @@ if( OPENGL_FOUND OR OPENGLES_FOUND )
|
||||
glslPatchCommon.glsl
|
||||
glslPatchBSpline.glsl
|
||||
glslPatchGregory.glsl
|
||||
glslPatchGregoryBasis.glsl
|
||||
glslPatchTransition.glsl
|
||||
glslPtexCommon.glsl
|
||||
)
|
||||
|
@ -105,12 +105,8 @@ CpuEvalLimitController::EvalLimitSample( LimitLocation const & coord,
|
||||
outQ, outDQU, outDQV );
|
||||
break;
|
||||
case Desc::GREGORY_BASIS : {
|
||||
Far::StencilTables const * stencils =
|
||||
ptables.GetEndCapStencilTables();
|
||||
assert(stencils and stencils->GetNumStencils()>0);
|
||||
evalGregoryBasis( pparam.bitField, s, t,
|
||||
*stencils,
|
||||
ptables.GetEndCapStencilIndex(*handle),
|
||||
cvs.begin(),
|
||||
vertexData.inDesc,
|
||||
vertexData.in,
|
||||
vertexData.outDesc,
|
||||
@ -206,12 +202,8 @@ CpuEvalLimitController::_EvalLimitSample( LimitLocation const & coords,
|
||||
out, outDu, outDv );
|
||||
break;
|
||||
case Desc::GREGORY_BASIS : {
|
||||
Far::StencilTables const * stencils =
|
||||
ptables.GetEndCapStencilTables();
|
||||
assert(stencils and stencils->GetNumStencils()>0);
|
||||
evalGregoryBasis( pparam.bitField, s, t,
|
||||
*stencils,
|
||||
ptables.GetEndCapStencilIndex(*handle),
|
||||
cvs.begin(),
|
||||
vertexData.inDesc,
|
||||
vertexData.in,
|
||||
vertexData.outDesc,
|
||||
@ -236,7 +228,8 @@ CpuEvalLimitController::_EvalLimitSample( LimitLocation const & coords,
|
||||
static int const zeroRings[6][4] = { {5, 6,10, 9}, // regular
|
||||
{1, 2, 6, 5}, // boundary / single-crease
|
||||
{1, 2, 5, 4}, // corner
|
||||
{0, 1, 2, 3} }; // no permutation
|
||||
{0, 1, 2, 3}, // no permutation
|
||||
{0, 5, 10, 15} }; // gregory basis
|
||||
|
||||
int const * permute = 0;
|
||||
switch (desc.GetType()) {
|
||||
@ -245,8 +238,8 @@ CpuEvalLimitController::_EvalLimitSample( LimitLocation const & coords,
|
||||
case Desc::BOUNDARY : permute = zeroRings[1]; break;
|
||||
case Desc::CORNER : permute = zeroRings[2]; break;
|
||||
case Desc::GREGORY :
|
||||
case Desc::GREGORY_BOUNDARY :
|
||||
case Desc::GREGORY_BASIS : permute = zeroRings[3]; break;
|
||||
case Desc::GREGORY_BOUNDARY : permute = zeroRings[3]; break;
|
||||
case Desc::GREGORY_BASIS : permute = zeroRings[4]; break;
|
||||
default:
|
||||
assert(0);
|
||||
};
|
||||
|
@ -372,8 +372,7 @@ evalCorner(Far::PatchParam::BitField bits,
|
||||
|
||||
void
|
||||
evalGregoryBasis(Far::PatchParam::BitField bits, float u, float v,
|
||||
Far::StencilTables const & basisStencils,
|
||||
int stencilIndex,
|
||||
Far::Index const * vertexIndices,
|
||||
VertexBufferDescriptor const & inDesc,
|
||||
float const * inQ,
|
||||
VertexBufferDescriptor const & outDesc,
|
||||
@ -390,10 +389,10 @@ evalGregoryBasis(Far::PatchParam::BitField bits, float u, float v,
|
||||
|
||||
float const *inOffset = inQ + inDesc.offset;
|
||||
|
||||
float * Q = outQ + outDesc.offset;
|
||||
outQ += outDesc.offset;
|
||||
|
||||
// clear result
|
||||
memset(Q, 0, length*sizeof(float));
|
||||
memset(outQ, 0, length*sizeof(float));
|
||||
if (outDQU) {
|
||||
memset(outDQU, 0, length*sizeof(float));
|
||||
}
|
||||
@ -404,22 +403,17 @@ evalGregoryBasis(Far::PatchParam::BitField bits, float u, float v,
|
||||
float uu = 1-u,
|
||||
vv = 1-v;
|
||||
// remark #1572: floating-point equality and inequality comparisons are unreliable
|
||||
#ifdef __INvEL_COMPILER
|
||||
#pragma warning diuable 1572
|
||||
#ifdef __INTEL_COMPILER
|
||||
#pragma warning disable 1572
|
||||
#endif
|
||||
float d11 = u+v; if(u+v==0.0f) d11 = 1.0f;
|
||||
float d12 = uu+v; if(uu+v==0.0f) d12 = 1.0f;
|
||||
float d21 = u+vv; if(u+vv==0.0f) d21 = 1.0f;
|
||||
float d22 = uu+vv; if(uu+vv==0.0f) d22 = 1.0f;
|
||||
#ifdef __INvEL_COMPILER
|
||||
#ifdef __INTEL_COMPILER
|
||||
#pragma warning enable 1572
|
||||
#endif
|
||||
|
||||
float weights[4][2] = { { u/d11, v/d11 },
|
||||
{ uu/d12, v/d12 },
|
||||
{ u/d21, vv/d21 },
|
||||
{ uu/d22, vv/d22 } };
|
||||
|
||||
//
|
||||
// P3 e3- e2+ P2
|
||||
// O--------O--------O--------O
|
||||
@ -441,89 +435,44 @@ evalGregoryBasis(Far::PatchParam::BitField bits, float u, float v,
|
||||
// P0 e0+ e1- P1
|
||||
//
|
||||
|
||||
float const *v3 = inOffset + vertexIndices[3]*inDesc.stride,
|
||||
*v4 = inOffset + vertexIndices[4]*inDesc.stride,
|
||||
*v8 = inOffset + vertexIndices[8]*inDesc.stride,
|
||||
*v9 = inOffset + vertexIndices[9]*inDesc.stride,
|
||||
*v13 = inOffset + vertexIndices[13]*inDesc.stride,
|
||||
*v14 = inOffset + vertexIndices[14]*inDesc.stride,
|
||||
*v18 = inOffset + vertexIndices[18]*inDesc.stride,
|
||||
*v19 = inOffset + vertexIndices[19]*inDesc.stride;
|
||||
|
||||
float *CP = (float*)alloca(inDesc.length*4*sizeof(float));
|
||||
|
||||
for (int k=0; k<inDesc.length; ++k) {
|
||||
CP[0*inDesc.length + k] = (u * v3[k] + v * v4[k])/d11;
|
||||
CP[1*inDesc.length + k] = (uu * v9[k] + v * v8[k])/d12;
|
||||
CP[2*inDesc.length + k] = (u * v19[k] + vv * v18[k])/d21;
|
||||
CP[3*inDesc.length + k] = (uu * v13[k] + vv * v14[k])/d22;
|
||||
}
|
||||
|
||||
// XXXX manuelk re-order stencils in factory and get rid of permutation ?
|
||||
static int const permute[16] =
|
||||
{ 0, 1, 7, 5, 2, -1, -1, 6, 16, -1, -1, 12, 15, 17, 11, 10 };
|
||||
{ 0, 1, 7, 5, 2, 3, 8, 6, 16, 18, 13, 12, 15, 17, 11, 10 };
|
||||
|
||||
int offset = stencilIndex;
|
||||
|
||||
for (int i=0, fcount=0; i<16; ++i) {
|
||||
for (int i=0; i<16; ++i) {
|
||||
|
||||
int index = permute[i];
|
||||
|
||||
if (index==-1) {
|
||||
float const * in = inOffset + vertexIndices[index]*inDesc.stride;
|
||||
if (index < 0) {
|
||||
in = &CP[-index-1];
|
||||
}
|
||||
|
||||
// 0-ring vertex: blend 2 extra basis CVs
|
||||
static int const fpermute[4][2] = { {3, 4}, {9, 8}, {19, 18}, {13, 14} };
|
||||
|
||||
assert(fcount < 4);
|
||||
int v0 = fpermute[fcount][0],
|
||||
v1 = fpermute[fcount][1];
|
||||
|
||||
Far::Stencil s0 = basisStencils.GetStencil(offset + v0),
|
||||
s1 = basisStencils.GetStencil(offset + v1);
|
||||
|
||||
float w0=weights[fcount][0],
|
||||
w1=weights[fcount][1];
|
||||
|
||||
{
|
||||
Far::Index const * srcIndices = s0.GetVertexIndices();
|
||||
float const * srcWeights = s0.GetWeights();
|
||||
for (int j=0; j<s0.GetSize(); ++j) {
|
||||
float const * in = inOffset + srcIndices[j]*inDesc.stride;
|
||||
float w = BU[i] * w0 * srcWeights[j],
|
||||
dw1 = DU[i] * w0 * srcWeights[j],
|
||||
dw2 = DV[i] * w0 * srcWeights[
|
||||
j];
|
||||
for (int k=0; k<length; ++k) {
|
||||
Q[k] += in[k] * w;
|
||||
for (int k=0; k<inDesc.length; ++k) {
|
||||
outQ[k] += BU[i] * in[k];
|
||||
if (outDQU) {
|
||||
outDQU[k] += in[k] * dw1;
|
||||
outDQU[k] += DU[i] * in[k];
|
||||
}
|
||||
if (outDQV) {
|
||||
outDQV[k] += in[k] * dw2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Far::Index const * srcIndices = s1.GetVertexIndices();
|
||||
float const * srcWeights = s1.GetWeights();
|
||||
for (int j=0; j<s1.GetSize(); ++j) {
|
||||
float const * in = inOffset + srcIndices[j]*inDesc.stride;
|
||||
float w = BU[i] * w1 * srcWeights[j],
|
||||
dw1 = DU[i] * w1 * srcWeights[j],
|
||||
dw2 = DV[i] * w1 * srcWeights[j];
|
||||
for (int k=0; k<length; ++k) {
|
||||
Q[k] += in[k] * w;
|
||||
if (outDQU) {
|
||||
outDQU[k] += in[k] * dw1;
|
||||
}
|
||||
if (outDQV) {
|
||||
outDQV[k] += in[k] * dw2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++fcount;
|
||||
} else {
|
||||
Far::Stencil s = basisStencils.GetStencil(offset + index);
|
||||
Far::Index const * srcIndices = s.GetVertexIndices();
|
||||
float const * srcWeights = s.GetWeights();
|
||||
for (int j=0; j<s.GetSize(); ++j) {
|
||||
float const * in = inOffset + srcIndices[j]*inDesc.stride;
|
||||
float w = BU[i] * srcWeights[j],
|
||||
dw1 = DU[i] * srcWeights[j],
|
||||
dw2 = DV[i] * srcWeights[j];
|
||||
for (int k=0; k<length; ++k) {
|
||||
Q[k] += in[k] * w;
|
||||
if (outDQU) {
|
||||
outDQU[k] += in[k] * dw1;
|
||||
}
|
||||
if (outDQV) {
|
||||
outDQV[k] += in[k] * dw2;
|
||||
}
|
||||
}
|
||||
outDQV[k] += DV[i] * in[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,9 +86,9 @@ evalCorner(Far::PatchParam::BitField bits,
|
||||
float * outDQV );
|
||||
|
||||
void
|
||||
evalGregoryBasis(Far::PatchParam::BitField bits, float u, float v,
|
||||
Far::StencilTables const & basisStencils,
|
||||
int stencilIndex,
|
||||
evalGregoryBasis(Far::PatchParam::BitField bits,
|
||||
float u, float v,
|
||||
Far::Index const *vertsIndices,
|
||||
VertexBufferDescriptor const & inDesc,
|
||||
float const * inQ,
|
||||
VertexBufferDescriptor const & outDesc,
|
||||
|
@ -52,6 +52,9 @@ static const char *bsplineShaderSource =
|
||||
static const char *gregoryShaderSource =
|
||||
#include "glslPatchGregory.gen.h"
|
||||
;
|
||||
static const char *gregoryBasisShaderSource =
|
||||
#include "glslPatchGregoryBasis.gen.h"
|
||||
;
|
||||
static const char *transitionShaderSource =
|
||||
#include "glslPatchTransition.gen.h"
|
||||
;
|
||||
@ -156,6 +159,17 @@ GLDrawRegistryBase::_CreateDrawSourceConfig(
|
||||
sconfig->tessEvalShader.AddDefine("OSD_PATCH_TESS_EVAL_GREGORY_SHADER");
|
||||
sconfig->tessEvalShader.AddDefine("OSD_PATCH_GREGORY_BOUNDARY");
|
||||
break;
|
||||
case Far::PatchDescriptor::GREGORY_BASIS:
|
||||
sconfig->vertexShader.source = gregoryBasisShaderSource;
|
||||
sconfig->vertexShader.version = "#version 410\n";
|
||||
sconfig->vertexShader.AddDefine("OSD_PATCH_VERTEX_GREGORY_BASIS_SHADER");
|
||||
sconfig->tessControlShader.source = gregoryBasisShaderSource;
|
||||
sconfig->tessControlShader.version = "#version 410\n";
|
||||
sconfig->tessControlShader.AddDefine("OSD_PATCH_TESS_CONTROL_GREGORY_BASIS_SHADER");
|
||||
sconfig->tessEvalShader.source = gregoryBasisShaderSource;
|
||||
sconfig->tessEvalShader.version = "#version 410\n";
|
||||
sconfig->tessEvalShader.AddDefine("OSD_PATCH_TESS_EVAL_GREGORY_BASIS_SHADER");
|
||||
break;
|
||||
default: // POINTS, LINES, QUADS, TRIANGLES
|
||||
// do nothing
|
||||
break;
|
||||
|
@ -68,14 +68,31 @@ public:
|
||||
_drawContext(0)
|
||||
{
|
||||
|
||||
GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive), bits.test(MeshUseSingleCreasePatch));
|
||||
GLMeshInterface::refineMesh(*_refiner, level,
|
||||
bits.test(MeshAdaptive),
|
||||
bits.test(MeshUseSingleCreasePatch));
|
||||
|
||||
int numElements =
|
||||
initializeVertexBuffers(numVertexElements, numVaryingElements, bits);
|
||||
int numVertexElementsInterleaved = numVertexElements +
|
||||
(bits.test(MeshInterleaveVarying) ? numVaryingElements : 0);
|
||||
int numVaryingElementsNonInterleaved =
|
||||
(bits.test(MeshInterleaveVarying) ? 0 : numVaryingElements);
|
||||
|
||||
initializeComputeContext(numVertexElements, numVaryingElements);
|
||||
initializeContext(numVertexElements, numVaryingElements,
|
||||
numVertexElementsInterleaved, level, bits);
|
||||
|
||||
initializeDrawContext(numElements, level, bits);
|
||||
int numVertices = GLMeshInterface::getNumVertices(*_refiner);
|
||||
|
||||
// FIXME: need a better API for numTotalVertices.
|
||||
if (_patchTables->GetEndCapVertexStencilTables()) {
|
||||
numVertices += _patchTables->GetEndCapVertexStencilTables()->GetNumStencils();
|
||||
}
|
||||
|
||||
initializeVertexBuffers(numVertices,
|
||||
numVertexElementsInterleaved,
|
||||
numVaryingElementsNonInterleaved);
|
||||
|
||||
// will retire soon
|
||||
_drawContext->UpdateVertexTexture(_vertexBuffer);
|
||||
}
|
||||
|
||||
Mesh(ComputeController * computeController,
|
||||
@ -170,8 +187,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void initializeComputeContext(int numVertexElements,
|
||||
int numVaryingElements ) {
|
||||
void initializeContext(int numVertexElements,
|
||||
int numVaryingElements,
|
||||
int numElements, int level, MeshBitset bits) {
|
||||
|
||||
assert(_refiner);
|
||||
|
||||
@ -185,7 +203,6 @@ private:
|
||||
|
||||
vertexStencils = Far::StencilTablesFactory::Create(*_refiner, options);
|
||||
|
||||
_kernelBatches.push_back(Far::StencilTablesFactory::Create(*vertexStencils));
|
||||
}
|
||||
|
||||
if (numVaryingElements>0) {
|
||||
@ -195,44 +212,69 @@ private:
|
||||
varyingStencils = Far::StencilTablesFactory::Create(*_refiner, options);
|
||||
}
|
||||
|
||||
_computeContext = ComputeContext::Create(vertexStencils, varyingStencils);
|
||||
assert(_refiner);
|
||||
Far::PatchTablesFactory::Options poptions(level);
|
||||
poptions.generateFVarTables = bits.test(MeshFVarData);
|
||||
poptions.useSingleCreasePatch = bits.test(MeshUseSingleCreasePatch);
|
||||
|
||||
if (bits.test(MeshUseGregoryBasis)) {
|
||||
poptions.adaptiveStencilTables = vertexStencils;
|
||||
poptions.adaptiveVaryingStencilTables = varyingStencils;
|
||||
}
|
||||
|
||||
_patchTables = Far::PatchTablesFactory::Create(*_refiner, poptions);
|
||||
|
||||
_drawContext = DrawContext::Create(_patchTables, numElements);
|
||||
|
||||
// XXX: factory API fix needed
|
||||
// merge greogry basis stencils
|
||||
Far::StencilTables const * endCapVertexStencils =
|
||||
_patchTables->GetEndCapVertexStencilTables();
|
||||
|
||||
if (endCapVertexStencils) {
|
||||
Far::StencilTables const * endCapVaryingStencils =
|
||||
_patchTables->GetEndCapVaryingStencilTables();
|
||||
|
||||
// concatinate vertexStencils and endCapStencils.
|
||||
// note that endCapStensils is owned by patchTable.
|
||||
Far::StencilTables const *inStencils[] = {
|
||||
vertexStencils, endCapVertexStencils
|
||||
};
|
||||
Far::StencilTables const *concatStencils =
|
||||
Far::StencilTablesFactory::Create(2, inStencils);
|
||||
|
||||
Far::StencilTables const *inVaryingStencils[] = {
|
||||
varyingStencils, endCapVaryingStencils
|
||||
};
|
||||
Far::StencilTables const *concatVaryingStencils =
|
||||
Far::StencilTablesFactory::Create(2, inVaryingStencils);
|
||||
|
||||
delete vertexStencils;
|
||||
vertexStencils = concatStencils;
|
||||
delete varyingStencils;
|
||||
varyingStencils = concatVaryingStencils;
|
||||
}
|
||||
|
||||
_kernelBatches.push_back(Far::StencilTablesFactory::Create(*vertexStencils));
|
||||
|
||||
_computeContext = ComputeContext::Create(vertexStencils,
|
||||
varyingStencils);
|
||||
|
||||
delete vertexStencils;
|
||||
delete varyingStencils;
|
||||
}
|
||||
|
||||
void initializeDrawContext(int numElements, int level, MeshBitset bits) {
|
||||
|
||||
assert(_refiner and _vertexBuffer);
|
||||
|
||||
Far::PatchTablesFactory::Options options(level);
|
||||
options.generateFVarTables = bits.test(MeshFVarData);
|
||||
options.useSingleCreasePatch = bits.test(MeshUseSingleCreasePatch);
|
||||
|
||||
_patchTables = Far::PatchTablesFactory::Create(*_refiner, options);
|
||||
|
||||
_drawContext = DrawContext::Create(_patchTables, numElements);
|
||||
|
||||
_drawContext->UpdateVertexTexture(_vertexBuffer);
|
||||
}
|
||||
|
||||
int initializeVertexBuffers(int numVertexElements,
|
||||
int numVaryingElements, MeshBitset bits) {
|
||||
|
||||
int numVertices = GLMeshInterface::getNumVertices(*_refiner);
|
||||
|
||||
int numElements = numVertexElements +
|
||||
(bits.test(MeshInterleaveVarying) ? numVaryingElements : 0);
|
||||
void initializeVertexBuffers(int numVertices,
|
||||
int numVertexElements,
|
||||
int numVaryingElements) {
|
||||
|
||||
if (numVertexElements) {
|
||||
|
||||
_vertexBuffer = VertexBuffer::Create(numElements, numVertices);
|
||||
_vertexBuffer = VertexBuffer::Create(numVertexElements, numVertices);
|
||||
}
|
||||
|
||||
if (numVaryingElements>0 and (not bits.test(MeshInterleaveVarying))) {
|
||||
if (numVaryingElements) {
|
||||
_varyingBuffer = VertexBuffer::Create(numVaryingElements, numVertices);
|
||||
}
|
||||
return numElements;
|
||||
}
|
||||
|
||||
Far::TopologyRefiner * _refiner;
|
||||
@ -280,14 +322,32 @@ public:
|
||||
{
|
||||
assert(_refiner);
|
||||
|
||||
GLMeshInterface::refineMesh(*_refiner, level, bits.test(MeshAdaptive), bits.test(MeshUseSingleCreasePatch));
|
||||
GLMeshInterface::refineMesh(*_refiner, level,
|
||||
bits.test(MeshAdaptive),
|
||||
bits.test(MeshUseSingleCreasePatch));
|
||||
|
||||
int numElements =
|
||||
initializeVertexBuffers(numVertexElements, numVaryingElements, bits);
|
||||
|
||||
initializeComputeContext(numVertexElements, numVaryingElements);
|
||||
int numVertexElementsInterleaved = numVertexElements +
|
||||
(bits.test(MeshInterleaveVarying) ? numVaryingElements : 0);
|
||||
int numVaryingElementsNonInterleaved =
|
||||
(bits.test(MeshInterleaveVarying) ? 0 : numVaryingElements);
|
||||
|
||||
initializeDrawContext(numElements, level, bits);
|
||||
initializeContext(numVertexElements, numVaryingElements,
|
||||
numVertexElementsInterleaved, level, bits);
|
||||
|
||||
int numVertices = GLMeshInterface::getNumVertices(*_refiner);
|
||||
|
||||
// FIXME: need better API for total number of vertices.
|
||||
if (_patchTables->GetEndCapVertexStencilTables()) {
|
||||
numVertices += _patchTables->GetEndCapVertexStencilTables()->GetNumStencils();
|
||||
}
|
||||
|
||||
initializeVertexBuffers(numVertices,
|
||||
numVertexElementsInterleaved,
|
||||
numVaryingElementsNonInterleaved);
|
||||
|
||||
// will retire
|
||||
_drawContext->UpdateVertexTexture(_vertexBuffer);
|
||||
}
|
||||
|
||||
Mesh(ComputeController * computeController,
|
||||
@ -382,9 +442,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void initializeComputeContext(int numVertexElements,
|
||||
int numVaryingElements ) {
|
||||
|
||||
void initializeContext(int numVertexElements,
|
||||
int numVaryingElements,
|
||||
int numElements, int level, MeshBitset bits) {
|
||||
assert(_refiner);
|
||||
|
||||
Far::StencilTablesFactory::Options options;
|
||||
@ -396,8 +456,6 @@ private:
|
||||
if (numVertexElements>0) {
|
||||
|
||||
vertexStencils = Far::StencilTablesFactory::Create(*_refiner, options);
|
||||
|
||||
_kernelBatches.push_back(Far::StencilTablesFactory::Create(*vertexStencils));
|
||||
}
|
||||
|
||||
if (numVaryingElements>0) {
|
||||
@ -409,41 +467,73 @@ private:
|
||||
|
||||
_computeContext = ComputeContext::Create(_clContext, vertexStencils, varyingStencils);
|
||||
|
||||
assert(_refiner);
|
||||
|
||||
Far::PatchTablesFactory::Options poptions(level);
|
||||
poptions.generateFVarTables = bits.test(MeshFVarData);
|
||||
poptions.useSingleCreasePatch = bits.test(MeshUseSingleCreasePatch);
|
||||
|
||||
// use gregory stencils
|
||||
if (bits.test(MeshUseGregoryBasis)) {
|
||||
poptions.adaptiveStencilTables = vertexStencils;
|
||||
poptions.adaptiveVaryingStencilTables = varyingStencils;
|
||||
}
|
||||
|
||||
_patchTables = Far::PatchTablesFactory::Create(*_refiner, poptions);
|
||||
|
||||
_drawContext = DrawContext::Create(_patchTables, numElements);
|
||||
|
||||
Far::StencilTables const *endCapVertexStencils =
|
||||
_patchTables->GetEndCapVertexStencilTables();
|
||||
|
||||
if (endCapVertexStencils) {
|
||||
Far::StencilTables const *endCapVaryingStencils =
|
||||
_patchTables->GetEndCapVaryingStencilTables();
|
||||
|
||||
// concatinate vertexStencils and endCapStencils.
|
||||
// note that endCapStensils is owned by patchTable.
|
||||
Far::StencilTables const *inStencils[] = {
|
||||
vertexStencils, endCapVertexStencils
|
||||
};
|
||||
|
||||
Far::StencilTables const *concatStencils =
|
||||
Far::StencilTablesFactory::Create(2, inStencils);
|
||||
|
||||
_kernelBatches.push_back(Far::StencilTablesFactory::Create(*concatStencils));
|
||||
|
||||
Far::StencilTables const *inVaryingStencils[] = {
|
||||
varyingStencils, endCapVaryingStencils
|
||||
};
|
||||
|
||||
Far::StencilTables const *concatVaryingStencils =
|
||||
Far::StencilTablesFactory::Create(2, inVaryingStencils);
|
||||
|
||||
delete vertexStencils;
|
||||
vertexStencils = concatStencils;
|
||||
delete varyingStencils;
|
||||
varyingStencils = concatVaryingStencils;
|
||||
}
|
||||
_kernelBatches.push_back(Far::StencilTablesFactory::Create(*vertexStencils));
|
||||
|
||||
_computeContext = ComputeContext::Create(_clContext,
|
||||
vertexStencils,
|
||||
varyingStencils);
|
||||
|
||||
delete vertexStencils;
|
||||
delete varyingStencils;
|
||||
}
|
||||
|
||||
void initializeDrawContext(int numElements, int level, MeshBitset bits) {
|
||||
|
||||
assert(_refiner and _vertexBuffer);
|
||||
|
||||
Far::PatchTablesFactory::Options options(level);
|
||||
options.generateFVarTables = bits.test(MeshFVarData);
|
||||
|
||||
_patchTables = Far::PatchTablesFactory::Create(*_refiner);
|
||||
|
||||
_drawContext = DrawContext::Create(_patchTables, numElements);
|
||||
|
||||
_drawContext->UpdateVertexTexture(_vertexBuffer);
|
||||
}
|
||||
|
||||
int initializeVertexBuffers(int numVertexElements,
|
||||
int numVaryingElements, MeshBitset bits) {
|
||||
|
||||
int numVertices = GLMeshInterface::getNumVertices(*_refiner);
|
||||
|
||||
int numElements = numVertexElements +
|
||||
(bits.test(MeshInterleaveVarying) ? numVaryingElements : 0);
|
||||
void initializeVertexBuffers(int numVertices,
|
||||
int numVertexElements,
|
||||
int numVaryingElements) {
|
||||
|
||||
if (numVertexElements) {
|
||||
|
||||
_vertexBuffer = VertexBuffer::Create(numElements, numVertices, _clContext);
|
||||
_vertexBuffer = VertexBuffer::Create(numVertexElements, numVertices, _clContext);
|
||||
}
|
||||
|
||||
if (numVaryingElements>0 and (not bits.test(MeshInterleaveVarying))) {
|
||||
if (numVaryingElements) {
|
||||
_varyingBuffer = VertexBuffer::Create(numVaryingElements, numVertices, _clContext);
|
||||
}
|
||||
return numElements;
|
||||
}
|
||||
|
||||
Far::TopologyRefiner * _refiner;
|
||||
|
@ -93,7 +93,9 @@ struct ControlVertex {
|
||||
vec4 position;
|
||||
centroid vec4 patchCoord; // u, v, level, faceID
|
||||
ivec4 ptexInfo; // U offset, V offset, 2^ptexlevel', rotation
|
||||
#ifdef OSD_ENABLE_PATCH_CULL
|
||||
ivec3 clipFlag;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct OutputVertex {
|
||||
|
294
opensubdiv/osd/glslPatchGregoryBasis.glsl
Normal file
294
opensubdiv/osd/glslPatchGregoryBasis.glsl
Normal file
@ -0,0 +1,294 @@
|
||||
//
|
||||
// Copyright 2015 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.
|
||||
//
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Patches.VertexGregoryBasis
|
||||
//----------------------------------------------------------
|
||||
#ifdef OSD_PATCH_VERTEX_GREGORY_BASIS_SHADER
|
||||
|
||||
layout(location = 0) in vec4 position;
|
||||
OSD_USER_VARYING_ATTRIBUTE_DECLARE
|
||||
|
||||
out block {
|
||||
ControlVertex v;
|
||||
OSD_USER_VARYING_DECLARE
|
||||
} outpt;
|
||||
|
||||
void main()
|
||||
{
|
||||
outpt.v.position = OsdModelViewMatrix() * position;
|
||||
outpt.v.patchCoord = vec4(0);
|
||||
outpt.v.ptexInfo = ivec4(0);
|
||||
OSD_PATCH_CULL_COMPUTE_CLIPFLAGS(position);
|
||||
OSD_USER_VARYING_PER_VERTEX();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Patches.TessControlGregoryBasis
|
||||
//----------------------------------------------------------
|
||||
#ifdef OSD_PATCH_TESS_CONTROL_GREGORY_BASIS_SHADER
|
||||
|
||||
layout(vertices = 20) out;
|
||||
|
||||
in block {
|
||||
ControlVertex v;
|
||||
OSD_USER_VARYING_DECLARE
|
||||
} inpt[];
|
||||
|
||||
out block {
|
||||
ControlVertex v;
|
||||
OSD_USER_VARYING_DECLARE
|
||||
} outpt[];
|
||||
|
||||
#define ID gl_InvocationID
|
||||
|
||||
void main()
|
||||
{
|
||||
outpt[ID].v = inpt[ID].v;
|
||||
OSD_USER_VARYING_PER_CONTROL_POINT(ID, ID);
|
||||
|
||||
int patchLevel = GetPatchLevel();
|
||||
|
||||
// +0.5 to avoid interpolation error of integer value
|
||||
outpt[ID].v.patchCoord = vec4(0, 0,
|
||||
patchLevel+0.5,
|
||||
GetPrimitiveID()+0.5);
|
||||
OSD_COMPUTE_PTEX_COORD_TESSCONTROL_SHADER;
|
||||
|
||||
if (ID == 0) {
|
||||
OSD_PATCH_CULL(OSD_PATCH_INPUT_SIZE);
|
||||
|
||||
// XXX: this metric is not consistent.
|
||||
// we will 1) compute the cage length as before
|
||||
// or 2) compute limit length for all patches.
|
||||
#ifdef OSD_ENABLE_SCREENSPACE_TESSELLATION
|
||||
gl_TessLevelOuter[0] =
|
||||
TessAdaptive(inpt[0].v.position.xyz, inpt[5].v.position.xyz);
|
||||
gl_TessLevelOuter[1] =
|
||||
TessAdaptive(inpt[0].v.position.xyz, inpt[15].v.position.xyz);
|
||||
gl_TessLevelOuter[2] =
|
||||
TessAdaptive(inpt[10].v.position.xyz, inpt[15].v.position.xyz);
|
||||
gl_TessLevelOuter[3] =
|
||||
TessAdaptive(inpt[5].v.position.xyz, inpt[10].v.position.xyz);
|
||||
|
||||
gl_TessLevelInner[0] =
|
||||
max(gl_TessLevelOuter[1], gl_TessLevelOuter[3]);
|
||||
gl_TessLevelInner[1] =
|
||||
max(gl_TessLevelOuter[0], gl_TessLevelOuter[2]);
|
||||
#else
|
||||
gl_TessLevelInner[0] = GetTessLevel(patchLevel);
|
||||
gl_TessLevelInner[1] = GetTessLevel(patchLevel);
|
||||
gl_TessLevelOuter[0] = GetTessLevel(patchLevel);
|
||||
gl_TessLevelOuter[1] = GetTessLevel(patchLevel);
|
||||
gl_TessLevelOuter[2] = GetTessLevel(patchLevel);
|
||||
gl_TessLevelOuter[3] = GetTessLevel(patchLevel);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Patches.TessEvalGregory
|
||||
//----------------------------------------------------------
|
||||
#ifdef OSD_PATCH_TESS_EVAL_GREGORY_BASIS_SHADER
|
||||
|
||||
layout(quads) in;
|
||||
layout(cw) in;
|
||||
|
||||
#if defined OSD_FRACTIONAL_ODD_SPACING
|
||||
layout(fractional_odd_spacing) in;
|
||||
#elif defined OSD_FRACTIONAL_EVEN_SPACING
|
||||
layout(fractional_even_spacing) in;
|
||||
#endif
|
||||
|
||||
in block {
|
||||
ControlVertex v;
|
||||
OSD_USER_VARYING_DECLARE
|
||||
} inpt[];
|
||||
|
||||
out block {
|
||||
OutputVertex v;
|
||||
OSD_USER_VARYING_DECLARE
|
||||
} outpt;
|
||||
|
||||
void main()
|
||||
{
|
||||
float u = gl_TessCoord.x,
|
||||
v = gl_TessCoord.y;
|
||||
|
||||
vec3 p[20];
|
||||
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
p[i] = inpt[i].v.position.xyz;
|
||||
}
|
||||
vec3 q[16];
|
||||
|
||||
float U = 1-u, V=1-v;
|
||||
|
||||
float d11 = u+v; if(u+v==0.0f) d11 = 1.0f;
|
||||
float d12 = U+v; if(U+v==0.0f) d12 = 1.0f;
|
||||
float d21 = u+V; if(u+V==0.0f) d21 = 1.0f;
|
||||
float d22 = U+V; if(U+V==0.0f) d22 = 1.0f;
|
||||
|
||||
#if 1
|
||||
q[ 5] = (u*p[3] + v*p[4])/d11;
|
||||
q[ 6] = (U*p[9] + v*p[8])/d12;
|
||||
q[ 9] = (u*p[19] + V*p[18])/d21;
|
||||
q[10] = (U*p[13] + V*p[14])/d22;
|
||||
#else
|
||||
q[ 5] = (p[3] + p[4])/2.0;
|
||||
q[ 6] = (p[9] + p[8])/2.0;
|
||||
q[ 9] = (p[19] + p[18])/2.0;
|
||||
q[10] = (p[13] + p[14])/2.0;
|
||||
#endif
|
||||
|
||||
q[ 0] = p[0];
|
||||
q[ 1] = p[1];
|
||||
q[ 2] = p[7];
|
||||
q[ 3] = p[5];
|
||||
q[ 4] = p[2];
|
||||
q[ 7] = p[6];
|
||||
q[ 8] = p[16];
|
||||
q[11] = p[12];
|
||||
q[12] = p[15];
|
||||
q[13] = p[17];
|
||||
q[14] = p[11];
|
||||
q[15] = p[10];
|
||||
|
||||
vec3 WorldPos = vec3(0, 0, 0);
|
||||
vec3 Tangent = vec3(0, 0, 0);
|
||||
vec3 BiTangent = vec3(0, 0, 0);
|
||||
|
||||
#ifdef OSD_COMPUTE_NORMAL_DERIVATIVES
|
||||
float B[4], D[4], C[4];
|
||||
vec3 BUCP[4] = vec3[4](vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0)),
|
||||
DUCP[4] = vec3[4](vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0)),
|
||||
CUCP[4] = vec3[4](vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0));
|
||||
vec3 dUU = vec3(0);
|
||||
vec3 dVV = vec3(0);
|
||||
vec3 dUV = vec3(0);
|
||||
|
||||
Univar4x4(u, B, D, C);
|
||||
|
||||
for (int i=0; i<4; ++i) {
|
||||
for (uint j=0; j<4; ++j) {
|
||||
// reverse face front
|
||||
vec3 A = q[i + 4*j];
|
||||
|
||||
BUCP[i] += A * B[j];
|
||||
DUCP[i] += A * D[j];
|
||||
CUCP[i] += A * C[j];
|
||||
}
|
||||
}
|
||||
|
||||
Univar4x4(v, B, D, C);
|
||||
|
||||
for (int i=0; i<4; ++i) {
|
||||
WorldPos += B[i] * BUCP[i];
|
||||
Tangent += B[i] * DUCP[i];
|
||||
BiTangent += D[i] * BUCP[i];
|
||||
dUU += B[i] * CUCP[i];
|
||||
dVV += C[i] * BUCP[i];
|
||||
dUV += D[i] * DUCP[i];
|
||||
}
|
||||
|
||||
int level = int(inpt[0].v.ptexInfo.z);
|
||||
BiTangent *= 3 * level;
|
||||
Tangent *= 3 * level;
|
||||
dUU *= 6 * level;
|
||||
dVV *= 6 * level;
|
||||
dUV *= 9 * level;
|
||||
|
||||
vec3 n = cross(BiTangent, Tangent);
|
||||
vec3 normal = normalize(n);
|
||||
|
||||
float E = dot(Tangent, Tangent);
|
||||
float F = dot(Tangent, BiTangent);
|
||||
float G = dot(BiTangent, BiTangent);
|
||||
float e = dot(normal, dUU);
|
||||
float f = dot(normal, dUV);
|
||||
float g = dot(normal, dVV);
|
||||
|
||||
vec3 Nu = (f*F-e*G)/(E*G-F*F) * Tangent + (e*F-f*E)/(E*G-F*F) * BiTangent;
|
||||
vec3 Nv = (g*F-f*G)/(E*G-F*F) * Tangent + (f*F-g*E)/(E*G-F*F) * BiTangent;
|
||||
|
||||
Nu = Nu/length(n) - n * (dot(Nu,n)/pow(dot(n,n), 1.5));
|
||||
Nv = Nv/length(n) - n * (dot(Nv,n)/pow(dot(n,n), 1.5));
|
||||
|
||||
outpt.v.Nu = Nu;
|
||||
outpt.v.Nv = Nv;
|
||||
|
||||
#else
|
||||
float B[4], D[4];
|
||||
vec3 BUCP[4] = vec3[4](vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0)),
|
||||
DUCP[4] = vec3[4](vec3(0,0,0), vec3(0,0,0), vec3(0,0,0), vec3(0,0,0));
|
||||
|
||||
Univar4x4(u, B, D);
|
||||
|
||||
for (int i=0; i<4; ++i) {
|
||||
for (uint j=0; j<4; ++j) {
|
||||
// reverse face front
|
||||
vec3 A = q[i + 4*j];
|
||||
|
||||
BUCP[i] += A * B[j];
|
||||
DUCP[i] += A * D[j];
|
||||
}
|
||||
}
|
||||
|
||||
Univar4x4(v, B, D);
|
||||
|
||||
for (int i=0; i<4; ++i) {
|
||||
WorldPos += B[i] * BUCP[i];
|
||||
Tangent += B[i] * DUCP[i];
|
||||
BiTangent += D[i] * BUCP[i];
|
||||
}
|
||||
int level = int(inpt[0].v.ptexInfo.z);
|
||||
BiTangent *= 3 * level;
|
||||
Tangent *= 3 * level;
|
||||
|
||||
vec3 normal = normalize(cross(BiTangent, Tangent));
|
||||
|
||||
#endif
|
||||
outpt.v.position = vec4(WorldPos, 1.0f);
|
||||
outpt.v.normal = normal;
|
||||
outpt.v.tangent = BiTangent;
|
||||
outpt.v.bitangent = Tangent;
|
||||
|
||||
//OSD_USER_VARYING_PER_EVAL_POINT(vec2(u,v), 0, 3, 1, 2);
|
||||
OSD_USER_VARYING_PER_EVAL_POINT(vec2(u,v), 0, 15, 5, 10);
|
||||
|
||||
outpt.v.patchCoord = inpt[0].v.patchCoord;
|
||||
outpt.v.patchCoord.xy = vec2(v, u);
|
||||
|
||||
OSD_COMPUTE_PTEX_COORD_TESSEVAL_SHADER;
|
||||
|
||||
OSD_DISPLACEMENT_CALLBACK;
|
||||
|
||||
gl_Position = OsdProjectionMatrix() * outpt.v.position;
|
||||
}
|
||||
|
||||
#endif
|
@ -51,7 +51,8 @@ enum MeshBits {
|
||||
MeshPtexData = 2,
|
||||
MeshFVarData = 3,
|
||||
MeshUseSingleCreasePatch = 4,
|
||||
NUM_MESH_BITS = 5,
|
||||
MeshUseGregoryBasis = 5,
|
||||
NUM_MESH_BITS = 6,
|
||||
};
|
||||
typedef std::bitset<NUM_MESH_BITS> MeshBitset;
|
||||
|
||||
|
21
regression/shapes/catmark_gregory_test0.h
Normal file
21
regression/shapes/catmark_gregory_test0.h
Normal file
@ -0,0 +1,21 @@
|
||||
static const std::string catmark_gregory_test0 = std::string(
|
||||
"# This file uses centimeters as units for non-parametric coordinates.\n"
|
||||
"v -0.500000 0.204097 -0.500000\n"
|
||||
"v 0.000000 0.117710 -0.500000\n"
|
||||
"v -0.500000 0.142210 -0.000000\n"
|
||||
"v 0.000000 0.000000 0.000000\n"
|
||||
"v -0.500000 0.204097 0.500000\n"
|
||||
"v 0.000000 0.117710 0.500000\n"
|
||||
"v 0.500000 0.348563 -0.003642\n"
|
||||
"vt -0.500000 -0.500000\n"
|
||||
"vt 0.000000 -0.500000\n"
|
||||
"vt -0.500000 -0.000000\n"
|
||||
"vt 0.000000 0.000000\n"
|
||||
"vt -0.500000 0.500000\n"
|
||||
"vt 0.000000 0.500000\n"
|
||||
"vt 0.500000 -0.003642\n"
|
||||
"s 1\n"
|
||||
"f 1/1/1 2/2/2 4/4/4 3/3/3\n"
|
||||
"f 3/3/3 4/4/4 6/6/6 5/5/5\n"
|
||||
"f 2/2/2 7/7/7 6/6/6 4/4/4\n"
|
||||
);
|
Loading…
Reference in New Issue
Block a user