mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2025-01-07 23:40:12 +00:00
391 lines
11 KiB
C++
391 lines
11 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.
|
|
//
|
|
|
|
#include "adaptiveEvaluator.h"
|
|
|
|
#define HBR_ADAPTIVE
|
|
#include "../hbr/mesh.h"
|
|
|
|
#include "../osd/vertex.h"
|
|
|
|
#ifdef OPENSUBDIV_HAS_OPENMP
|
|
#include <omp.h>
|
|
#include "../osd/ompComputeController.h"
|
|
#endif
|
|
|
|
|
|
#include "../osd/cpuComputeController.h"
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
using namespace OpenSubdiv;
|
|
using namespace std;
|
|
|
|
|
|
PxOsdUtilAdaptiveEvaluator::PxOsdUtilAdaptiveEvaluator():
|
|
_refiner(NULL),
|
|
_ownsRefiner(false),
|
|
_computeContext(NULL),
|
|
_evalLimitContext(NULL),
|
|
_vertexBuffer(NULL),
|
|
_vvBuffer(NULL),
|
|
_vbufP(NULL),
|
|
_vbufdPdu(NULL),
|
|
_vbufdPdv(NULL),
|
|
_pOutput(NULL),
|
|
_dPduOutput(NULL),
|
|
_dPdvOutput(NULL)
|
|
{
|
|
}
|
|
|
|
PxOsdUtilAdaptiveEvaluator::~PxOsdUtilAdaptiveEvaluator()
|
|
{
|
|
if (_ownsRefiner and _refiner) {
|
|
delete _refiner;
|
|
}
|
|
if (_computeContext)
|
|
delete _computeContext;
|
|
if (_evalLimitContext)
|
|
delete _evalLimitContext;
|
|
if (_vertexBuffer)
|
|
delete _vertexBuffer;
|
|
if (_vvBuffer)
|
|
delete _vvBuffer;
|
|
if (_vbufP)
|
|
delete _vbufP;
|
|
if (_vbufdPdu)
|
|
delete _vbufdPdu;
|
|
if (_vbufdPdv)
|
|
delete _vbufdPdv;
|
|
}
|
|
|
|
|
|
bool
|
|
PxOsdUtilAdaptiveEvaluator::Initialize(
|
|
const PxOsdUtilSubdivTopology &t,
|
|
string *errorMessage)
|
|
{
|
|
|
|
// create and initialize a refiner, passing "true" for adaptive
|
|
// to indicate we wish for adaptive refinement rather than uniform
|
|
PxOsdUtilRefiner *refiner = new PxOsdUtilRefiner();
|
|
_ownsRefiner = true;
|
|
|
|
if (not refiner->Initialize(t, true, errorMessage)) {
|
|
return false;
|
|
}
|
|
|
|
return Initialize(refiner, errorMessage);
|
|
}
|
|
|
|
bool
|
|
PxOsdUtilAdaptiveEvaluator::Initialize(
|
|
PxOsdUtilRefiner *refiner,
|
|
string *errorMessage)
|
|
{
|
|
|
|
if (refiner->GetAdaptive()) {
|
|
if (errorMessage)
|
|
*errorMessage = "Adaptive evaluator requires adaptive refiner";
|
|
return false;
|
|
}
|
|
|
|
// Note we assume someone else keeps this pointer alive
|
|
_refiner = refiner;
|
|
_ownsRefiner = false;
|
|
|
|
const FarMesh<OsdVertex> *fmesh = _refiner->GetFarMesh();
|
|
const HbrMesh<OsdVertex> *hmesh = _refiner->GetHbrMesh();
|
|
|
|
if (not (fmesh and hmesh)) {
|
|
if (errorMessage)
|
|
*errorMessage = "No valid adaptive far/hbr mesh";
|
|
return false;
|
|
}
|
|
|
|
|
|
_computeContext = OsdCpuComputeContext::Create(fmesh);
|
|
|
|
// Three elements (x/y/z) per refined point at every subdivision level
|
|
// defined by the farMesh. The coarse vertices seed the beginning of
|
|
// this buffer, and Refine populates the rest based on subdivision
|
|
_vertexBuffer = OsdCpuVertexBuffer::Create(
|
|
3, fmesh->GetNumVertices());
|
|
|
|
// zeros
|
|
memset( _vertexBuffer->BindCpuBuffer(), 0,
|
|
3 * fmesh->GetNumVertices() * sizeof(float));
|
|
|
|
/*
|
|
const vector<string> &vvNames = _refiner->GetTopology().vvNames;
|
|
// If needed, allocate vertex buffer for other vertex varying
|
|
// values, like UVs or gprim data.
|
|
if (vvNames.size()) {
|
|
|
|
// One element in the vertex buffer for each
|
|
// named vertex varying attribute in the refined mesh
|
|
_vvBuffer = OsdCpuVertexBuffer::Create(
|
|
(int)vvNames.size(), fmesh->GetNumVertices());
|
|
|
|
// zeros
|
|
memset( _vvBuffer->BindCpuBuffer(), 0,
|
|
vvNames.size() * fmesh->GetNumVertices() * sizeof(float));
|
|
}
|
|
*/
|
|
|
|
// A context object used to store data used in refinement
|
|
_computeContext = OsdCpuComputeContext::Create(fmesh);
|
|
|
|
// A context object used to store data used in fast limit surface
|
|
// evaluation. This contains vectors of patches and associated
|
|
// tables pulled and computed from the adaptive farMesh.
|
|
// It also holds onto vertex buffer data through binds
|
|
_evalLimitContext = OsdCpuEvalLimitContext::Create(
|
|
fmesh, /*requierFVarData*/ false);
|
|
|
|
// A buffer with one float per target point to use when
|
|
// evaluating interpolated weights
|
|
OsdCpuVertexBuffer* _vbufP = OsdCpuVertexBuffer::Create(3, 1);
|
|
OsdCpuVertexBuffer* _vbufdPdu = OsdCpuVertexBuffer::Create(3, 1);
|
|
OsdCpuVertexBuffer* _vbufdPdv = OsdCpuVertexBuffer::Create(3, 1);
|
|
_pOutput = _vbufP->BindCpuBuffer();
|
|
_dPduOutput = _vbufdPdu->BindCpuBuffer();
|
|
_dPdvOutput = _vbufdPdv->BindCpuBuffer();
|
|
|
|
memset( (void*)_pOutput, 0, 3 * sizeof(float));
|
|
memset( (void*)_dPduOutput, 0, 3 * sizeof(float));
|
|
memset( (void*)_dPdvOutput, 0, 3 * sizeof(float));
|
|
|
|
// Setup evaluation context. Values are offset, length, stride */
|
|
OsdVertexBufferDescriptor in_desc(0, 3, 3), out_desc(0, 3, 3);
|
|
_evalLimitContext->GetVertexData().Bind(in_desc, _vertexBuffer, out_desc,
|
|
_vbufP, _vbufdPdu, _vbufdPdv);
|
|
|
|
std::cout << "Initialized adaptive evaluator\n";
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
PxOsdUtilAdaptiveEvaluator::SetCoarsePositions(
|
|
const float *coords, int numFloats, string *errorMessage )
|
|
{
|
|
//XXX: should be >= num coarse vertices
|
|
if (numFloats/3 >= _refiner->GetFarMesh()->GetNumVertices()) {
|
|
if (errorMessage)
|
|
*errorMessage = "Indexing error in tesselator";
|
|
} else {
|
|
_vertexBuffer->UpdateData(coords, 0, numFloats / 3);
|
|
}
|
|
}
|
|
|
|
bool
|
|
PxOsdUtilAdaptiveEvaluator::Refine(
|
|
int numThreads, string *errorMessage)
|
|
{
|
|
const FarMesh<OsdVertex> *fmesh = _refiner->GetFarMesh();
|
|
|
|
#ifdef OPENSUBDIV_HAS_OPENMP
|
|
|
|
if (numThreads > 1) {
|
|
OsdOmpComputeController ompComputeController(numThreads);
|
|
ompComputeController.Refine(_computeContext,
|
|
fmesh->GetKernelBatches(),
|
|
_vertexBuffer, _vvBuffer);
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
OsdCpuComputeController cpuComputeController;
|
|
cpuComputeController.Refine(_computeContext,
|
|
fmesh->GetKernelBatches(),
|
|
_vertexBuffer, _vvBuffer);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PxOsdUtilAdaptiveEvaluator::EvaluateLimit(
|
|
const OsdEvalCoords &coords, float P[3], float dPdu[3], float dPdv[3])
|
|
{
|
|
|
|
// This controller is an empty object, essentially a namespace.
|
|
OsdCpuEvalLimitController cpuEvalLimitController;
|
|
|
|
cpuEvalLimitController.
|
|
EvalLimitSample<OsdCpuVertexBuffer, OsdCpuVertexBuffer>(
|
|
coords, _evalLimitContext, 0 /*index*/);
|
|
|
|
// Copy results from vertex buffers into return parameters
|
|
memcpy(P, _pOutput, sizeof(float) * 3);
|
|
if (dPdu) {
|
|
memcpy(dPdu, _dPduOutput, sizeof(float) * 3);
|
|
}
|
|
if (dPdv) {
|
|
memcpy(dPdv, _dPdvOutput, sizeof(float) * 3);
|
|
}
|
|
}
|
|
|
|
|
|
void ccgSubSurf__mapGridToFace(int S, float grid_u, float grid_v,
|
|
float *face_u, float *face_v)
|
|
{
|
|
float u, v;
|
|
|
|
/* - Each grid covers half of the face along the edges.
|
|
* - Grid's (0, 0) starts from the middle of the face.
|
|
*/
|
|
u = 0.5f - 0.5f * grid_u;
|
|
v = 0.5f - 0.5f * grid_v;
|
|
|
|
if (S == 0) {
|
|
*face_u = v;
|
|
*face_v = u;
|
|
}
|
|
else if (S == 1) {
|
|
*face_u = 1.0f - u;
|
|
*face_v = v;
|
|
}
|
|
else if (S == 2) {
|
|
*face_u = 1.0f - v;
|
|
*face_v = 1.0f - u;
|
|
}
|
|
else {
|
|
*face_u = v;
|
|
*face_v = 1.0f - u;
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
PxOsdUtilAdaptiveEvaluator::GetRefinedTopology(
|
|
PxOsdUtilSubdivTopology *out,
|
|
//positions will have three floats * t->numVertices
|
|
std::vector<float> *positions,
|
|
std::string *errorMessage)
|
|
{
|
|
|
|
const PxOsdUtilSubdivTopology &t = GetTopology();
|
|
|
|
positions->clear();
|
|
|
|
// XXX: dirk
|
|
// What are correct values for subfaceGridSize?
|
|
// These look good.
|
|
// Power of 2 + 1?
|
|
int gridSize = 5;
|
|
int subfaceGridSize = 3;
|
|
|
|
int coarseFaceIndex = 0;
|
|
int ptexFaceIndex = 0;
|
|
int numSubfacesToProcess = 0;
|
|
|
|
bool done = false;
|
|
|
|
while (not done ) {
|
|
|
|
// iterate through faces in coarse topology. Four sided
|
|
// faces are traversed in one pass, sub-faces of non quads
|
|
// are traversed in order, i.e. three subfaces for each triangle
|
|
// These are the indices used by ptex and the eval API
|
|
bool subface = false;
|
|
|
|
if (numSubfacesToProcess > 0) {
|
|
// We are iterating over sub-faces of a non-quad
|
|
numSubfacesToProcess--;
|
|
subface = true;
|
|
} else {
|
|
int vertsInCoarseFace = t.nverts[coarseFaceIndex++];
|
|
|
|
if (vertsInCoarseFace != 4) {
|
|
// Non quads are subdivided by putting a point
|
|
// in the middle of the face and creating
|
|
// subfaces.
|
|
numSubfacesToProcess = vertsInCoarseFace-1;
|
|
subface = true;
|
|
}
|
|
}
|
|
|
|
int startingPositionIndex = (int) positions->size();
|
|
int currentGridSize = gridSize;
|
|
|
|
// Subfaces have a smaller gridsize so tessellation lines up.
|
|
if (subface)
|
|
currentGridSize = subfaceGridSize;
|
|
|
|
OsdEvalCoords coords;
|
|
coords.face = ptexFaceIndex;
|
|
|
|
for (int x = 0; x < currentGridSize; x++) {
|
|
for (int y = 0; y < currentGridSize; y++) {
|
|
float grid_u = (float) x / (currentGridSize - 1),
|
|
grid_v = (float) y / (currentGridSize - 1);
|
|
|
|
float P[3];
|
|
|
|
coords.u = grid_u;
|
|
coords.v = grid_v;
|
|
|
|
EvaluateLimit(coords, P, NULL, NULL);
|
|
positions->push_back(P[0]);
|
|
positions->push_back(P[1]);
|
|
positions->push_back(P[2]);
|
|
|
|
// If not on edges, add a quad
|
|
if ( (x<currentGridSize-1) and (y<currentGridSize-1)) {
|
|
int v[4];
|
|
int vBase = startingPositionIndex/3;
|
|
v[0] = vBase + x*currentGridSize + y;
|
|
v[1] = vBase + x*currentGridSize + y+1;
|
|
v[2] = vBase + (x+1)*currentGridSize+y+1;
|
|
v[3] = vBase + (x+1)*currentGridSize+y;
|
|
out->AddFace(4, v);
|
|
}
|
|
}
|
|
}
|
|
|
|
ptexFaceIndex++;
|
|
|
|
if ((numSubfacesToProcess == 0) and
|
|
(coarseFaceIndex == (int)t.nverts.size())) {
|
|
done = true; // last face
|
|
}
|
|
|
|
} // while (not done)
|
|
|
|
out->name = GetTopology().name + "_refined";
|
|
out->numVertices = (int) positions->size()/3;
|
|
out->refinementLevel = GetTopology().refinementLevel;
|
|
|
|
return out->IsValid(errorMessage);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|