mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-25 01:01:15 +00:00
Added adaptiveEvaluator class inspired by Sergey's work in blender.
Do feature adaptive refinement, then use the cpuEvalLimit API to evaluate grids of points on faces. Test harness is tessellateObjFile which has a -blender option to trigger the gridding tessellation code.
This commit is contained in:
parent
8b65c1b2f4
commit
7e0f6955c7
@ -28,7 +28,7 @@ if( OPENGL_FOUND AND (GLEW_FOUND AND GLFW_FOUND) OR (APPLE AND GLFW_FOUND))
|
||||
add_subdirectory(glStencilViewer)
|
||||
add_subdirectory(simpleCpu)
|
||||
add_subdirectory(limitEval)
|
||||
add_subdirectory(projectTest)
|
||||
add_subdirectory(tessellateObjFile)
|
||||
if (NOT APPLE)
|
||||
add_subdirectory(uvViewer)
|
||||
endif()
|
||||
|
@ -61,18 +61,17 @@ set(PLATFORM_LIBRARIES
|
||||
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}/opensubdiv
|
||||
${PROJECT_SOURCE_DIR}/examples
|
||||
)
|
||||
|
||||
_add_executable(projectTest
|
||||
_add_executable(tessellateObjFile
|
||||
main.cpp
|
||||
)
|
||||
|
||||
|
||||
target_link_libraries(projectTest
|
||||
target_link_libraries(tessellateObjFile
|
||||
osdutil
|
||||
${PLATFORM_LIBRARIES}
|
||||
)
|
||||
|
||||
install(TARGETS projectTest DESTINATION ${CMAKE_BINDIR_BASE})
|
||||
install(TARGETS tessellateObjFile DESTINATION ${CMAKE_BINDIR_BASE})
|
||||
|
@ -59,7 +59,7 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <osdutil/adaptiveEvaluator.h>
|
||||
#include <osdutil/uniformEvaluator.h>
|
||||
#include <osdutil/topology.h>
|
||||
|
||||
@ -78,7 +78,7 @@ using namespace OpenSubdiv;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static bool
|
||||
createOsdMesh(char *inputFile, char *outputFile, std::string *errorMessage)
|
||||
uniformTessellate(char *inputFile, char *outputFile, std::string *errorMessage)
|
||||
{
|
||||
|
||||
PxOsdUtilSubdivTopology topology;
|
||||
@ -90,8 +90,6 @@ createOsdMesh(char *inputFile, char *outputFile, std::string *errorMessage)
|
||||
|
||||
topology.refinementLevel = 2;
|
||||
|
||||
std::cout << "Did read topology\n";
|
||||
|
||||
PxOsdUtilUniformEvaluator uniformEvaluator;
|
||||
|
||||
// Create uniformEvaluator
|
||||
@ -103,13 +101,12 @@ createOsdMesh(char *inputFile, char *outputFile, std::string *errorMessage)
|
||||
// Push the vertex data
|
||||
uniformEvaluator.SetCoarsePositions(pointPositions, errorMessage);
|
||||
|
||||
// Refine with eight threads
|
||||
// Refine with one thread
|
||||
if (not uniformEvaluator.Refine(1, errorMessage)) {
|
||||
std::cout << "Refine failed with " << *errorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Refine with eight threads
|
||||
|
||||
PxOsdUtilSubdivTopology refinedTopology;
|
||||
const float *positions = NULL;
|
||||
|
||||
@ -127,6 +124,55 @@ createOsdMesh(char *inputFile, char *outputFile, std::string *errorMessage)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
blenderStyleTessellate(char *inputFile, char *outputFile, std::string *errorMessage)
|
||||
{
|
||||
|
||||
PxOsdUtilSubdivTopology topology;
|
||||
std::vector<float> pointPositions;
|
||||
|
||||
if (not topology.ReadFromObjFile(inputFile, &pointPositions, errorMessage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
topology.refinementLevel = 5;
|
||||
|
||||
PxOsdUtilAdaptiveEvaluator adaptiveEvaluator;
|
||||
|
||||
// Create adaptiveEvaluator
|
||||
if (not adaptiveEvaluator.Initialize(topology, errorMessage)) {
|
||||
std::cout << "Initialize failed with " << *errorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Push the vertex data
|
||||
adaptiveEvaluator.SetCoarsePositions(
|
||||
&(pointPositions[0]), pointPositions.size(), errorMessage);
|
||||
|
||||
// Refine with one thread
|
||||
if (not adaptiveEvaluator.Refine(1, errorMessage)) {
|
||||
std::cout << "Refine failed with " << *errorMessage << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
PxOsdUtilSubdivTopology refinedTopology;
|
||||
std::vector<float> positions;
|
||||
|
||||
if (not adaptiveEvaluator.GetRefinedTopology(
|
||||
&refinedTopology, &positions, errorMessage)) {
|
||||
std::cout << "GetRefinedTopology failed with " << *errorMessage <<"\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not refinedTopology.WriteObjFile(
|
||||
outputFile, &(positions[0]), errorMessage)) {
|
||||
std::cout << errorMessage << std::endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void
|
||||
callbackError(OpenSubdiv::OsdErrorType err, const char *message)
|
||||
@ -140,20 +186,23 @@ callbackError(OpenSubdiv::OsdErrorType err, const char *message)
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
|
||||
if (argc < 3) {
|
||||
std::cout << "Usage: projectTest input.obj output\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "input is " << argv[1] << " and output is " << argv[2] <<std::endl;
|
||||
|
||||
|
||||
OsdSetErrorCallback(callbackError);
|
||||
|
||||
std::string errorMessage;
|
||||
|
||||
if (not createOsdMesh(argv[1], argv[2], &errorMessage)) {
|
||||
std::cout << "Failed with error: " << errorMessage << std::endl;
|
||||
if ((argc == 4) and (std::string(argv[1]) == std::string("-blender"))) {
|
||||
std::cout << "About to call blender-style tessellate\n";
|
||||
if (not blenderStyleTessellate(argv[2], argv[3], &errorMessage)) {
|
||||
std::cout << "Failed with error: " << errorMessage << std::endl;
|
||||
return -1;
|
||||
}
|
||||
} else if (argc != 3) {
|
||||
std::cout << "Usage: tessellate [-blender] input.obj output.obj\n";
|
||||
return false;
|
||||
} else {
|
||||
if (not uniformTessellate(argv[1], argv[2], &errorMessage)) {
|
||||
std::cout << "Failed with error: " << errorMessage << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -31,6 +31,7 @@ include_directories(
|
||||
# source & headers
|
||||
|
||||
set(PUBLIC_HEADER_FILES
|
||||
adaptiveEvaluator.h
|
||||
batch.h
|
||||
drawItem.h
|
||||
drawController.h
|
||||
@ -41,6 +42,8 @@ set(PUBLIC_HEADER_FILES
|
||||
)
|
||||
|
||||
add_library(osdutil
|
||||
adaptiveEvaluator.h
|
||||
adaptiveEvaluator.cpp
|
||||
mesh.h
|
||||
mesh.cpp
|
||||
refiner.h
|
||||
|
390
opensubdiv/osdutil/adaptiveEvaluator.cpp
Normal file
390
opensubdiv/osdutil/adaptiveEvaluator.cpp
Normal file
@ -0,0 +1,390 @@
|
||||
//
|
||||
// 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 = 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 == t.nverts.size())) {
|
||||
done = true; // last face
|
||||
}
|
||||
|
||||
} // while (not done)
|
||||
|
||||
out->name = GetTopology().name + "_refined";
|
||||
out->numVertices = positions->size()/3;
|
||||
out->refinementLevel = GetTopology().refinementLevel;
|
||||
|
||||
return out->IsValid(errorMessage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
143
opensubdiv/osdutil/adaptiveEvaluator.h
Normal file
143
opensubdiv/osdutil/adaptiveEvaluator.h
Normal file
@ -0,0 +1,143 @@
|
||||
//
|
||||
// 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 PXOSDUTIL_ADAPTIVE_EVALUATOR_H
|
||||
#define PXOSDUTIL_ADAPTIVE_EVALUATOR_H
|
||||
|
||||
#include "refiner.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define HBR_ADAPTIVE
|
||||
|
||||
#include "../osd/cpuVertexBuffer.h"
|
||||
#include "../osd/cpuComputeContext.h"
|
||||
#include "../osd/cpuEvalLimitController.h"
|
||||
#include "../osd/cpuEvalLimitContext.h"
|
||||
#include "../far/mesh.h"
|
||||
|
||||
// This class takes a mesh that has undergone adaptive refinement to
|
||||
// create bspline and gregory patches to a fixed subdivision level,
|
||||
// and creates required run time OpenSubdiv data structures used to
|
||||
// call the eval API to sample values on subdivision surfaces on the
|
||||
// limit surface..
|
||||
//
|
||||
class PxOsdUtilAdaptiveEvaluator {
|
||||
public:
|
||||
PxOsdUtilAdaptiveEvaluator();
|
||||
|
||||
~PxOsdUtilAdaptiveEvaluator();
|
||||
|
||||
// Initialize returns false on error. If errorMessage is non-NULL it'll
|
||||
// be populated upon error.
|
||||
//
|
||||
// If successful vertex buffers and compute contexts will have been
|
||||
// created and are ready to SetCoarse* methods, call Refine, and then
|
||||
// sample refined values with the Get* methods
|
||||
//
|
||||
// Note that calling this method assumes that the evaluator isn't
|
||||
// responsible for the refiner's lifetime, someone else needs to
|
||||
// hold onto the refiner pointer. This allows for lightweight sharing
|
||||
// of refiners among evaluators.
|
||||
//
|
||||
bool Initialize(
|
||||
PxOsdUtilRefiner* refiner,
|
||||
std::string *errorMessage = NULL);
|
||||
|
||||
bool Initialize(
|
||||
const PxOsdUtilSubdivTopology &topology,
|
||||
std::string *errorMessage = NULL);
|
||||
|
||||
// Set new coarse-mesh CV positions, need to call Refine
|
||||
// before calling Get* methods. Three floats per
|
||||
// point packed.
|
||||
void SetCoarsePositions(
|
||||
const float *coords, int numFloats,
|
||||
std::string *errorMessage = NULL
|
||||
);
|
||||
|
||||
// Refine the coarse mesh, needed before calling GetPositions, GetQuads etc
|
||||
// If numThreads is 1, use single cpu. If numThreads > 1 use Omp and set
|
||||
// number of omp threads.
|
||||
//
|
||||
bool Refine(int numThreads,
|
||||
std::string *errorMessage = NULL);
|
||||
|
||||
|
||||
void EvaluateLimit(
|
||||
const OpenSubdiv::OsdEvalCoords &coords,
|
||||
float P[3], float dPdu[3], float dPdv[3]);
|
||||
|
||||
bool GetRefinedTopology(
|
||||
PxOsdUtilSubdivTopology *t,
|
||||
//positions will have three floats * t->numVertices
|
||||
std::vector<float> *positions,
|
||||
std::string *errorMessage = NULL);
|
||||
|
||||
// Forward these calls through to the refiner, which may forward
|
||||
// to the mesh. Make these top level API calls on the evaluator
|
||||
// so clients can talk to a single API
|
||||
//
|
||||
const std::string &GetName() const { return _refiner->GetName();}
|
||||
|
||||
const OpenSubdiv::HbrMesh<OpenSubdiv::OsdVertex> *GetHbrMesh() {
|
||||
return _refiner->GetHbrMesh();
|
||||
}
|
||||
|
||||
const PxOsdUtilSubdivTopology &GetTopology() const {
|
||||
return _refiner->GetTopology();
|
||||
}
|
||||
|
||||
const OpenSubdiv::FarMesh<OpenSubdiv::OsdVertex>* GetFarMesh() {
|
||||
return _refiner->GetFarMesh();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// A pointer to the shared refiner used. Note that this class may
|
||||
// own the refiner pointer (if _ownsRefiner is true), or it may
|
||||
// assume that someone else is responsible for managing that pointer
|
||||
// if _ownsRefiner is false.
|
||||
PxOsdUtilRefiner *_refiner;
|
||||
bool _ownsRefiner;
|
||||
|
||||
OpenSubdiv::OsdCpuComputeContext *_computeContext;
|
||||
OpenSubdiv::OsdCpuEvalLimitContext *_evalLimitContext;
|
||||
OpenSubdiv::OsdCpuVertexBuffer *_vertexBuffer;
|
||||
OpenSubdiv::OsdCpuVertexBuffer *_vvBuffer; // not yet used
|
||||
|
||||
OpenSubdiv::OsdCpuVertexBuffer *_vbufP;
|
||||
OpenSubdiv::OsdCpuVertexBuffer *_vbufdPdu;
|
||||
OpenSubdiv::OsdCpuVertexBuffer *_vbufdPdv;
|
||||
|
||||
const float *_pOutput;
|
||||
const float *_dPduOutput;
|
||||
const float *_dPdvOutput;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* PXOSDUTIL_ADAPTIVE_EVALUATOR_H */
|
Loading…
Reference in New Issue
Block a user