mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-27 22:10:06 +00:00
This change adds simple HbrMesh and FarMesh wrapper classes to osdutil
and a projectTest example binary to test. Currently projectTest crashes, Manuel has been looking into the crash- thanks!
This commit is contained in:
parent
404ec9649d
commit
38399a382c
@ -28,7 +28,10 @@ if( OPENGL_FOUND AND (GLEW_FOUND AND GLFW_FOUND) OR (APPLE AND GLFW_FOUND))
|
|||||||
add_subdirectory(glStencilViewer)
|
add_subdirectory(glStencilViewer)
|
||||||
add_subdirectory(simpleCpu)
|
add_subdirectory(simpleCpu)
|
||||||
add_subdirectory(limitEval)
|
add_subdirectory(limitEval)
|
||||||
add_subdirectory(uvViewer)
|
add_subdirectory(projectTest)
|
||||||
|
if (NOT APPLE)
|
||||||
|
add_subdirectory(uvViewer)
|
||||||
|
endif()
|
||||||
if(OPENGL_4_3_FOUND AND (NOT APPLE))
|
if(OPENGL_4_3_FOUND AND (NOT APPLE))
|
||||||
# the paintTest example requires GL functionality not available on OSX
|
# the paintTest example requires GL functionality not available on OSX
|
||||||
add_subdirectory(paintTest)
|
add_subdirectory(paintTest)
|
||||||
|
79
examples/projectTest/CMakeLists.txt
Normal file
79
examples/projectTest/CMakeLists.txt
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) Pixar. All rights reserved.
|
||||||
|
#
|
||||||
|
# This license governs use of the accompanying software. If you
|
||||||
|
# use the software, you accept this license. If you do not accept
|
||||||
|
# the license, do not use the software.
|
||||||
|
#
|
||||||
|
# 1. Definitions
|
||||||
|
# The terms "reproduce," "reproduction," "derivative works," and
|
||||||
|
# "distribution" have the same meaning here as under U.S.
|
||||||
|
# copyright law. A "contribution" is the original software, or
|
||||||
|
# any additions or changes to the software.
|
||||||
|
# A "contributor" is any person or entity that distributes its
|
||||||
|
# contribution under this license.
|
||||||
|
# "Licensed patents" are a contributor's patent claims that read
|
||||||
|
# directly on its contribution.
|
||||||
|
#
|
||||||
|
# 2. Grant of Rights
|
||||||
|
# (A) Copyright Grant- Subject to the terms of this license,
|
||||||
|
# including the license conditions and limitations in section 3,
|
||||||
|
# each contributor grants you a non-exclusive, worldwide,
|
||||||
|
# royalty-free copyright license to reproduce its contribution,
|
||||||
|
# prepare derivative works of its contribution, and distribute
|
||||||
|
# its contribution or any derivative works that you create.
|
||||||
|
# (B) Patent Grant- Subject to the terms of this license,
|
||||||
|
# including the license conditions and limitations in section 3,
|
||||||
|
# each contributor grants you a non-exclusive, worldwide,
|
||||||
|
# royalty-free license under its licensed patents to make, have
|
||||||
|
# made, use, sell, offer for sale, import, and/or otherwise
|
||||||
|
# dispose of its contribution in the software or derivative works
|
||||||
|
# of the contribution in the software.
|
||||||
|
#
|
||||||
|
# 3. Conditions and Limitations
|
||||||
|
# (A) No Trademark License- This license does not grant you
|
||||||
|
# rights to use any contributor's name, logo, or trademarks.
|
||||||
|
# (B) If you bring a patent claim against any contributor over
|
||||||
|
# patents that you claim are infringed by the software, your
|
||||||
|
# patent license from such contributor to the software ends
|
||||||
|
# automatically.
|
||||||
|
# (C) If you distribute any portion of the software, you must
|
||||||
|
# retain all copyright, patent, trademark, and attribution
|
||||||
|
# notices that are present in the software.
|
||||||
|
# (D) If you distribute any portion of the software in source
|
||||||
|
# code form, you may do so only under this license by including a
|
||||||
|
# complete copy of this license with your distribution. If you
|
||||||
|
# distribute any portion of the software in compiled or object
|
||||||
|
# code form, you may only do so under a license that complies
|
||||||
|
# with this license.
|
||||||
|
# (E) The software is licensed "as-is." You bear the risk of
|
||||||
|
# using it. The contributors give no express warranties,
|
||||||
|
# guarantees or conditions. You may have additional consumer
|
||||||
|
# rights under your local laws which this license cannot change.
|
||||||
|
# To the extent permitted under your local laws, the contributors
|
||||||
|
# exclude the implied warranties of merchantability, fitness for
|
||||||
|
# a particular purpose and non-infringement.
|
||||||
|
#
|
||||||
|
|
||||||
|
set(PLATFORM_LIBRARIES
|
||||||
|
${OSD_LINK_TARGET}
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${PROJECT_SOURCE_DIR}/opensubdiv
|
||||||
|
${PROJECT_SOURCE_DIR}/regression
|
||||||
|
${PROJECT_SOURCE_DIR}/examples
|
||||||
|
)
|
||||||
|
|
||||||
|
_add_executable(projectTest
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
target_link_libraries(projectTest
|
||||||
|
osdutil
|
||||||
|
${PLATFORM_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS projectTest DESTINATION ${CMAKE_BINDIR_BASE})
|
||||||
|
|
214
examples/projectTest/main.cpp
Normal file
214
examples/projectTest/main.cpp
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
//
|
||||||
|
// Copyright (C) Pixar. All rights reserved.
|
||||||
|
//
|
||||||
|
// This license governs use of the accompanying software. If you
|
||||||
|
// use the software, you accept this license. If you do not accept
|
||||||
|
// the license, do not use the software.
|
||||||
|
//
|
||||||
|
// 1. Definitions
|
||||||
|
// The terms "reproduce," "reproduction," "derivative works," and
|
||||||
|
// "distribution" have the same meaning here as under U.S.
|
||||||
|
// copyright law. A "contribution" is the original software, or
|
||||||
|
// any additions or changes to the software.
|
||||||
|
// A "contributor" is any person or entity that distributes its
|
||||||
|
// contribution under this license.
|
||||||
|
// "Licensed patents" are a contributor's patent claims that read
|
||||||
|
// directly on its contribution.
|
||||||
|
//
|
||||||
|
// 2. Grant of Rights
|
||||||
|
// (A) Copyright Grant- Subject to the terms of this license,
|
||||||
|
// including the license conditions and limitations in section 3,
|
||||||
|
// each contributor grants you a non-exclusive, worldwide,
|
||||||
|
// royalty-free copyright license to reproduce its contribution,
|
||||||
|
// prepare derivative works of its contribution, and distribute
|
||||||
|
// its contribution or any derivative works that you create.
|
||||||
|
// (B) Patent Grant- Subject to the terms of this license,
|
||||||
|
// including the license conditions and limitations in section 3,
|
||||||
|
// each contributor grants you a non-exclusive, worldwide,
|
||||||
|
// royalty-free license under its licensed patents to make, have
|
||||||
|
// made, use, sell, offer for sale, import, and/or otherwise
|
||||||
|
// dispose of its contribution in the software or derivative works
|
||||||
|
// of the contribution in the software.
|
||||||
|
//
|
||||||
|
// 3. Conditions and Limitations
|
||||||
|
// (A) No Trademark License- This license does not grant you
|
||||||
|
// rights to use any contributor's name, logo, or trademarks.
|
||||||
|
// (B) If you bring a patent claim against any contributor over
|
||||||
|
// patents that you claim are infringed by the software, your
|
||||||
|
// patent license from such contributor to the software ends
|
||||||
|
// automatically.
|
||||||
|
// (C) If you distribute any portion of the software, you must
|
||||||
|
// retain all copyright, patent, trademark, and attribution
|
||||||
|
// notices that are present in the software.
|
||||||
|
// (D) If you distribute any portion of the software in source
|
||||||
|
// code form, you may do so only under this license by including a
|
||||||
|
// complete copy of this license with your distribution. If you
|
||||||
|
// distribute any portion of the software in compiled or object
|
||||||
|
// code form, you may only do so under a license that complies
|
||||||
|
// with this license.
|
||||||
|
// (E) The software is licensed "as-is." You bear the risk of
|
||||||
|
// using it. The contributors give no express warranties,
|
||||||
|
// guarantees or conditions. You may have additional consumer
|
||||||
|
// rights under your local laws which this license cannot change.
|
||||||
|
// To the extent permitted under your local laws, the contributors
|
||||||
|
// exclude the implied warranties of merchantability, fitness for
|
||||||
|
// a particular purpose and non-infringement.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <osd/cpuComputeContext.h>
|
||||||
|
#include <osd/cpuComputeController.h>
|
||||||
|
#include <osd/cpuEvalLimitContext.h>
|
||||||
|
#include <osd/cpuEvalLimitController.h>
|
||||||
|
#include <osd/cpuVertexBuffer.h>
|
||||||
|
#include <osd/error.h>
|
||||||
|
#include <osd/mesh.h>
|
||||||
|
#include <osd/vertex.h>
|
||||||
|
|
||||||
|
#include <osdutil/mesh.h>
|
||||||
|
#include <osdutil/refiner.h>
|
||||||
|
|
||||||
|
#include "../common/stopwatch.h"
|
||||||
|
|
||||||
|
#include <cfloat>
|
||||||
|
#include <vector>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef OPENSUBDIV_HAS_OPENMP
|
||||||
|
#include <omp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace OpenSubdiv;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
typedef HbrMesh<OsdVertex> OsdHbrMesh;
|
||||||
|
typedef HbrVertex<OsdVertex> OsdHbrVertex;
|
||||||
|
typedef HbrFace<OsdVertex> OsdHbrFace;
|
||||||
|
typedef HbrHalfedge<OsdVertex> OsdHbrHalfedge;
|
||||||
|
|
||||||
|
typedef FarMesh<OsdVertex> OsdFarMesh;
|
||||||
|
typedef FarMeshFactory<OsdVertex> OsdFarMeshFactory;
|
||||||
|
typedef FarSubdivisionTables<OsdVertex> OsdFarMeshSubdivision;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
static void
|
||||||
|
createOsdMesh(int level)
|
||||||
|
{
|
||||||
|
float points[] = { 0.000000, -1.414214, 1.000000,
|
||||||
|
1.414214, 0.000000, 1.000000,
|
||||||
|
-1.414214, 0.000000, 1.000000,
|
||||||
|
0.000000, 1.414214, 1.000000,
|
||||||
|
-1.414214, 0.000000, -1.000000,
|
||||||
|
0.000000, 1.414214, -1.000000,
|
||||||
|
0.000000, -1.414214, -1.000000,
|
||||||
|
1.414214, 0.000000, -1.000000};
|
||||||
|
|
||||||
|
int nverts[] = { 4, 4, 4, 4, 4, 4};
|
||||||
|
|
||||||
|
int indices[] = { 0, 1, 3, 2,
|
||||||
|
2, 3, 5, 4,
|
||||||
|
4, 5, 7, 6,
|
||||||
|
6, 7, 1, 0,
|
||||||
|
1, 7, 5, 3,
|
||||||
|
6, 0, 2, 4};
|
||||||
|
|
||||||
|
|
||||||
|
// Scheme scheme = kCatmark;
|
||||||
|
|
||||||
|
PxOsdUtilSubdivTopology t;
|
||||||
|
t.name = "TestSubdiv";
|
||||||
|
for (int i=0; i< (int)(sizeof(nverts)/sizeof(int)); ++i) {
|
||||||
|
t.nverts.push_back(nverts[i]);
|
||||||
|
}
|
||||||
|
for (int i=0; i< (int)(sizeof(indices)/sizeof(int)); ++i) {
|
||||||
|
t.indices.push_back(indices[i]);
|
||||||
|
}
|
||||||
|
t.numVertices = (int)sizeof(points)/(3*sizeof(float));
|
||||||
|
t.maxLevels = 8;
|
||||||
|
|
||||||
|
std::string errorMessage;
|
||||||
|
PxOsdUtilRefiner refiner;
|
||||||
|
|
||||||
|
// Create refiner, passing "false" to adaptive so we'll get
|
||||||
|
// uniform refinement
|
||||||
|
if (not refiner.Initialize(t, false, &errorMessage)) {
|
||||||
|
std::cout << "Refiner creation failed with " << errorMessage << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> refinedQuads;
|
||||||
|
if (not refiner.GetRefinedQuads(&refinedQuads, &errorMessage)) {
|
||||||
|
std::cout << "GetRefinedQuads failed with " << errorMessage << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << "Quads = " << refinedQuads.size()/4 << std::endl;
|
||||||
|
for (int i=0; i<(int)refinedQuads.size(); i+=4) {
|
||||||
|
std::cout << "(" << refinedQuads[i] <<
|
||||||
|
", " << refinedQuads[i+1] <<
|
||||||
|
", " << refinedQuads[i+2] <<
|
||||||
|
", " << refinedQuads[i+3] <<
|
||||||
|
")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
// Push the vertex data:
|
||||||
|
std::vector<float> pointsVec;
|
||||||
|
pointsVec.resize(sizeof(points));
|
||||||
|
for (int i=0; i<(int)sizeof(points); ++i) {
|
||||||
|
pointsVec[i] = points[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
shape->SetCoarsePositions(pointsVec);
|
||||||
|
|
||||||
|
std::vector<float> refinedPositions;
|
||||||
|
|
||||||
|
|
||||||
|
if (not (shape->Refine(2) and
|
||||||
|
shape->GetPositions(&refinedPositions, &errorMessage) and
|
||||||
|
shape->GetQuads(&refinedQuads, &errorMessage))) {
|
||||||
|
std::cout << errorMessage << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Hot damn, it worked.\n";
|
||||||
|
std::cout << "Positions = " << refinedPositions.size()/3 << std::endl;
|
||||||
|
for (int i=0; i<(int)refinedPositions.size(); i+=3) {
|
||||||
|
std::cout << "(" << refinedPositions[i] <<
|
||||||
|
", " << refinedPositions[i+1] <<
|
||||||
|
"," << refinedPositions[i+2] << ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (not shape->WriteRefinedObj("foo.obj", &errorMessage)) {
|
||||||
|
std::cout << errorMessage << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
static void
|
||||||
|
callbackError(OpenSubdiv::OsdErrorType err, const char *message)
|
||||||
|
{
|
||||||
|
printf("OsdError: %d\n", err);
|
||||||
|
printf("%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
int main(int, char**) {
|
||||||
|
|
||||||
|
|
||||||
|
OsdSetErrorCallback(callbackError);
|
||||||
|
|
||||||
|
createOsdMesh(1);
|
||||||
|
|
||||||
|
}
|
@ -34,8 +34,26 @@ set(PUBLIC_HEADER_FILES
|
|||||||
batch.h
|
batch.h
|
||||||
drawItem.h
|
drawItem.h
|
||||||
drawController.h
|
drawController.h
|
||||||
|
mesh.h
|
||||||
|
refiner.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_library(osdutil
|
||||||
|
mesh.h
|
||||||
|
mesh.cpp
|
||||||
|
refiner.h
|
||||||
|
refiner.cpp
|
||||||
|
${INC_FILES}
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(osdutil PROPERTIES OUTPUT_NAME osdutil CLEAN_DIRECT_OUTPUT 1)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
osdutil
|
||||||
|
${PLATFORM_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
set(DOXY_HEADER_FILES ${PUBLIC_HEADER_FILES})
|
set(DOXY_HEADER_FILES ${PUBLIC_HEADER_FILES})
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
|
540
opensubdiv/osdutil/mesh.cpp
Normal file
540
opensubdiv/osdutil/mesh.cpp
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
#include "mesh.h"
|
||||||
|
|
||||||
|
#include <osd/vertex.h>
|
||||||
|
#include <hbr/catmark.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace OpenSubdiv;
|
||||||
|
|
||||||
|
static HbrCatmarkSubdivision<OsdVertex> _catmark;
|
||||||
|
|
||||||
|
|
||||||
|
PxOsdUtilSubdivTopology::PxOsdUtilSubdivTopology():
|
||||||
|
name("noname"),
|
||||||
|
numVertices(0),
|
||||||
|
maxLevels(2) // arbitrary, start with a reasonable subdivision level
|
||||||
|
{
|
||||||
|
std::cout << "Creating subdiv topology object\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
PxOsdUtilSubdivTopology::~PxOsdUtilSubdivTopology()
|
||||||
|
{
|
||||||
|
std::cout << "Destroying subdiv topology object\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
PxOsdUtilSubdivTopology::Initialize(
|
||||||
|
int numVerticesParam,
|
||||||
|
const int *nvertsParam, int numFaces,
|
||||||
|
const int *indicesParam, int indicesLen,
|
||||||
|
int levels,
|
||||||
|
string *errorMessage)
|
||||||
|
{
|
||||||
|
|
||||||
|
numVertices = numVerticesParam;
|
||||||
|
maxLevels = levels;
|
||||||
|
|
||||||
|
nverts.resize(numFaces);
|
||||||
|
for (int i=0; i<numFaces; ++i) {
|
||||||
|
nverts[i] = nvertsParam[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
indices.resize(indicesLen);
|
||||||
|
for (int i=0; i<indicesLen; ++i) {
|
||||||
|
indices[i] = indicesParam[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsValid(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PxOsdUtilSubdivTopology::IsValid(string *errorMessage) const
|
||||||
|
{
|
||||||
|
if (numVertices == 0) {
|
||||||
|
if (errorMessage) {
|
||||||
|
stringstream ss;
|
||||||
|
ss << "Topology " << name << " has no vertices";
|
||||||
|
*errorMessage = ss.str();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<(int)indices.size(); ++i) {
|
||||||
|
if ((indices[i] < 0) or
|
||||||
|
(indices[i] >= numVertices)) {
|
||||||
|
if (errorMessage) {
|
||||||
|
stringstream ss;
|
||||||
|
ss << "Topology " << name << " has bad index " << indices[i] << " at index " << i;
|
||||||
|
*errorMessage = ss.str();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalNumIndices = 0;
|
||||||
|
for (int i=0; i< (int)nverts.size(); ++i) {
|
||||||
|
if (nverts[i] < 1) {
|
||||||
|
if (errorMessage) {
|
||||||
|
stringstream ss;
|
||||||
|
ss << "Topology " << name << " has bad nverts " << nverts[i] << " at index " << i;
|
||||||
|
*errorMessage = ss.str();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
totalNumIndices += nverts[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalNumIndices != (int)indices.size()) {
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = "Bad indexing for face topology";
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PxOsdUtilSubdivTopology::Print() const
|
||||||
|
{
|
||||||
|
std::cout << "Mesh " << name << "\n";
|
||||||
|
std::cout << "\tnumVertices = " << numVertices << "\n";
|
||||||
|
std::cout << "\tmaxLevels = " << maxLevels << "\n";
|
||||||
|
std::cout << "\tindices ( " << indices.size() << ") : ";
|
||||||
|
for (int i=0; i<(int)indices.size(); ++i) {
|
||||||
|
std::cout << indices[i] << ", ";
|
||||||
|
}
|
||||||
|
std::cout << "\n";
|
||||||
|
|
||||||
|
std::cout << "\tnverts ( " << nverts.size() << ") : ";
|
||||||
|
for (int i=0; i<(int)nverts.size(); ++i) {
|
||||||
|
std::cout << nverts[i] << ", ";
|
||||||
|
}
|
||||||
|
std::cout << "\n";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PxOsdUtilMesh::PxOsdUtilMesh(
|
||||||
|
const PxOsdUtilSubdivTopology &topology,
|
||||||
|
std::string *errorMessage):
|
||||||
|
_t(topology),
|
||||||
|
_hmesh(NULL),
|
||||||
|
_valid(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (not topology.IsValid(errorMessage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_t.fvNames.empty()) {
|
||||||
|
|
||||||
|
std::cout << "Creating NON face varying hbrmesh\n";
|
||||||
|
|
||||||
|
_hmesh = new HbrMesh<OsdVertex>(&_catmark);
|
||||||
|
} else {
|
||||||
|
int fvarcount = (int) _t.fvNames.size();
|
||||||
|
|
||||||
|
// For now we only handle 1 float per FV variable.
|
||||||
|
_fvarwidths.assign(fvarcount, 1);
|
||||||
|
|
||||||
|
int startIndex = 0;
|
||||||
|
for (int fvarindex = 0; fvarindex < fvarcount; ++fvarindex) {
|
||||||
|
_fvarindices.push_back(startIndex);
|
||||||
|
_fvaroffsets[_t.fvNames[fvarindex]] = startIndex;
|
||||||
|
startIndex += _fvarwidths[fvarindex];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << "Creating face varying hbrmesh\n";
|
||||||
|
|
||||||
|
_hmesh = new HbrMesh<OsdVertex>(
|
||||||
|
&_catmark, fvarcount, &_fvarindices[0],
|
||||||
|
&_fvarwidths[0], fvarcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
OsdVertex v;
|
||||||
|
for (int i = 0; i < _t.numVertices; ++i) {
|
||||||
|
HbrVertex<OsdVertex>* hvert = _hmesh->NewVertex(i, OsdVertex());
|
||||||
|
if (!hvert) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage = "Unable to create call NewVertex for Hbr";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Created " << _t.numVertices << " vertices for hbr mesh\n";
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
int fvarWidth = _hmesh->GetTotalFVarWidth();
|
||||||
|
|
||||||
|
std::cout << "Total fvarWidth = " << fvarWidth << "\n";
|
||||||
|
|
||||||
|
if (_t.fvData.size() < _t.nverts.size() * fvarWidth ||
|
||||||
|
fvarWidth != (int)_t.fvNames.size()) {
|
||||||
|
/*XXX if (errorMessage)
|
||||||
|
*errorMessage = TfStringPrintf(
|
||||||
|
"Incorrectly sized face data: name count = %d, "
|
||||||
|
"data width = %d, face count = %d, total data size = %d.",
|
||||||
|
(int) _t.fvNames.size(),
|
||||||
|
fvarWidth,
|
||||||
|
(int) _t.nverts.size(),
|
||||||
|
(int) _t.fvData.size());
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ptex index is not necessarily the same as the face index
|
||||||
|
int ptexIndex = 0;
|
||||||
|
|
||||||
|
// face-vertex count offset
|
||||||
|
int fvcOffset = 0;
|
||||||
|
|
||||||
|
int facesCreated = 0;
|
||||||
|
for (int i=0; i<(int)_t.nverts.size(); ++i) {
|
||||||
|
int nv = _t.nverts[i];
|
||||||
|
|
||||||
|
/*XXX No loop yet
|
||||||
|
if ((_scheme==kLoop) and (nv!=3)) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage = TfStringPrintf(
|
||||||
|
"Trying to create a Loop surbd with non-triangle face\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
for(int j=0;j<nv;j++) {
|
||||||
|
HbrVertex<OsdVertex> * origin =
|
||||||
|
_hmesh->GetVertex(_t.indices[fvcOffset + j]);
|
||||||
|
HbrVertex<OsdVertex> * destination =
|
||||||
|
_hmesh->GetVertex(_t.indices[fvcOffset + (j+1)%nv] );
|
||||||
|
HbrHalfedge<OsdVertex> * opposite = destination->GetEdge(origin);
|
||||||
|
|
||||||
|
if(origin==NULL || destination==NULL) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage =
|
||||||
|
" An edge was specified that connected a nonexistent vertex";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(origin == destination) {
|
||||||
|
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage =
|
||||||
|
" An edge was specified that connected a vertex to itself";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(opposite && opposite->GetOpposite() ) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage =
|
||||||
|
" A non-manifold edge incident to more than 2 faces was found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(origin->GetEdge(destination)) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage =
|
||||||
|
" An edge connecting two vertices was specified more than once."
|
||||||
|
" It's likely that an incident face was flipped\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Creating face with indices at offset " << fvcOffset << " ";
|
||||||
|
for (int k=0; k<nv; ++k) {
|
||||||
|
std::cout << _t.indices[fvcOffset+k] << " ";
|
||||||
|
}
|
||||||
|
std::cout << "\n";
|
||||||
|
|
||||||
|
HbrFace<OsdVertex>* hface = _hmesh->NewFace(
|
||||||
|
nv, &(_t.indices[fvcOffset]), 0);
|
||||||
|
|
||||||
|
|
||||||
|
++facesCreated;
|
||||||
|
|
||||||
|
if (!hface) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage = "Unable to create Hbr face";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ptex index isn't a straight-up polygon index; rather,
|
||||||
|
// it's an index into a "minimally quadrangulated" base mesh.
|
||||||
|
// Take all non-rect polys and subdivide them once.
|
||||||
|
hface->SetPtexIndex(ptexIndex);
|
||||||
|
ptexIndex += (nv == 4) ? 1 : nv;
|
||||||
|
|
||||||
|
// prideout: 3/21/2013 - Inspired by "GetFVarData" in examples/mayaViewer/hbrUtil.cpp
|
||||||
|
if (!_t.fvNames.empty()) {
|
||||||
|
std::cout << "found fvNames!\n";
|
||||||
|
|
||||||
|
const float* faceData = &(_t.fvData[fvcOffset*fvarWidth]);
|
||||||
|
for (int fvi = 0; fvi < nv; ++fvi) {
|
||||||
|
int vindex = _t.indices[fvi + fvcOffset];
|
||||||
|
HbrVertex<OsdVertex>* v = _hmesh->GetVertex(vindex);
|
||||||
|
HbrFVarData<OsdVertex>& fvarData = v->GetFVarData(hface);
|
||||||
|
if (!fvarData.IsInitialized()) {
|
||||||
|
fvarData.SetAllData(fvarWidth, faceData);
|
||||||
|
} else if (!fvarData.CompareAll(fvarWidth, faceData)) {
|
||||||
|
|
||||||
|
// If data exists for this face vertex, but is different
|
||||||
|
// (e.g. we're on a UV seam) create another fvar datum
|
||||||
|
HbrFVarData<OsdVertex>& fvarData = v->NewFVarData(hface);
|
||||||
|
fvarData.SetAllData(fvarWidth, faceData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance pointer to next set of face-varying data
|
||||||
|
faceData += fvarWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fvcOffset += nv;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Create " << facesCreated << " faces in hbrMesh\n";
|
||||||
|
|
||||||
|
_ProcessTagsAndFinishMesh(
|
||||||
|
_hmesh, _t.tagData.tags, _t.tagData.numArgs, _t.tagData.intArgs,
|
||||||
|
_t.tagData.floatArgs, _t.tagData.stringArgs);
|
||||||
|
|
||||||
|
_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PxOsdUtilMesh::~PxOsdUtilMesh()
|
||||||
|
{
|
||||||
|
std::cout << "Deleting PxOsdUtilMesh\n";
|
||||||
|
delete _hmesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessTagsAndFinishMesh(...)
|
||||||
|
// This translates prman-style lists of tags into OSD method calls.
|
||||||
|
//
|
||||||
|
// prideout: 3/19/2013 - since tidSceneRenderer has a similar
|
||||||
|
// function, we should factor this into an amber utility, or
|
||||||
|
// into osd itself. I'd vote for the latter. It already has
|
||||||
|
// a shapeUtils in its regression suite that almost fits the bill.
|
||||||
|
//
|
||||||
|
// prideout: 3/19/2013 - edits are not yet supported.
|
||||||
|
void
|
||||||
|
PxOsdUtilMesh::_ProcessTagsAndFinishMesh(
|
||||||
|
HbrMesh<OsdVertex> *mesh,
|
||||||
|
const vector<string> &tags,
|
||||||
|
const vector<int> &numArgs,
|
||||||
|
const vector<int> &intArgs,
|
||||||
|
const vector<float> &floatArgs,
|
||||||
|
const vector<string> &stringArgs)
|
||||||
|
{
|
||||||
|
mesh->SetInterpolateBoundaryMethod(HbrMesh<OsdVertex>::k_InterpolateBoundaryEdgeOnly);
|
||||||
|
|
||||||
|
const int* currentInt = &intArgs[0];
|
||||||
|
const float* currentFloat = &floatArgs[0];
|
||||||
|
const string* currentString = &stringArgs[0];
|
||||||
|
|
||||||
|
// TAGS (crease, corner, hole, smooth triangles, edits(vertex,
|
||||||
|
// edge, face), creasemethod, facevaryingpropagatecorners, interpolateboundary
|
||||||
|
for(int i = 0; i < (int)tags.size(); ++i){
|
||||||
|
const char * tag = tags[i].c_str();
|
||||||
|
int nint = numArgs[3*i];
|
||||||
|
int nfloat = numArgs[3*i+1];
|
||||||
|
int nstring = numArgs[3*i+2];
|
||||||
|
|
||||||
|
// XXX could use tokens here to reduce string matching overhead
|
||||||
|
if(strcmp(tag, "interpolateboundary") == 0) {
|
||||||
|
// Interp boundaries
|
||||||
|
assert(nint == 1);
|
||||||
|
switch(currentInt[0]) {
|
||||||
|
case 0:
|
||||||
|
mesh->SetInterpolateBoundaryMethod(HbrMesh<OsdVertex>::k_InterpolateBoundaryNone);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
mesh->SetInterpolateBoundaryMethod(HbrMesh<OsdVertex>::k_InterpolateBoundaryEdgeAndCorner);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mesh->SetInterpolateBoundaryMethod(HbrMesh<OsdVertex>::k_InterpolateBoundaryEdgeOnly);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*XXX
|
||||||
|
TF_WARN("Subdivmesh contains unknown interpolate boundary method: %d\n",
|
||||||
|
currentInt[0]);
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Processing of this tag is done in mesh->Finish()
|
||||||
|
} else if(strcmp(tag, "crease") == 0) {
|
||||||
|
for(int j = 0; j < nint-1; ++j) {
|
||||||
|
// Find the appropriate edge
|
||||||
|
HbrVertex<OsdVertex>* v = mesh->GetVertex(currentInt[j]);
|
||||||
|
HbrVertex<OsdVertex>* w = mesh->GetVertex(currentInt[j+1]);
|
||||||
|
HbrHalfedge<OsdVertex>* e = NULL;
|
||||||
|
if(v && w) {
|
||||||
|
e = v->GetEdge(w);
|
||||||
|
if(!e) {
|
||||||
|
// The halfedge might be oriented the other way
|
||||||
|
e = w->GetEdge(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!e) {
|
||||||
|
/*XXX
|
||||||
|
TF_WARN("Subdivmesh has non-existent sharp edge (%d,%d).\n",
|
||||||
|
currentInt[j], currentInt[j+1]);
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
e->SetSharpness(std::max(0.0f, ((nfloat > 1) ? currentFloat[j] : currentFloat[0])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(strcmp(tag, "corner") == 0) {
|
||||||
|
for(int j = 0; j < nint; ++j) {
|
||||||
|
HbrVertex<OsdVertex>* v = mesh->GetVertex(currentInt[j]);
|
||||||
|
if(v) {
|
||||||
|
v->SetSharpness(std::max(0.0f, ((nfloat > 1) ? currentFloat[j] : currentFloat[0])));
|
||||||
|
} else {
|
||||||
|
/*XXX
|
||||||
|
TF_WARN("Subdivmesh has non-existent sharp vertex %d.\n", currentInt[j]);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(strcmp(tag, "hole") == 0) {
|
||||||
|
for(int j = 0; j < nint; ++j) {
|
||||||
|
HbrFace<OsdVertex>* f = mesh->GetFace(currentInt[j]);
|
||||||
|
if(f) {
|
||||||
|
f->SetHole();
|
||||||
|
} else {
|
||||||
|
/*XXX
|
||||||
|
TF_WARN("Subdivmesh has hole at non-existent face %d.\n",
|
||||||
|
currentInt[j]);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(strcmp(tag, "facevaryinginterpolateboundary") == 0) {
|
||||||
|
switch(currentInt[0]) {
|
||||||
|
case 0:
|
||||||
|
mesh->SetFVarInterpolateBoundaryMethod(HbrMesh<OsdVertex>::k_InterpolateBoundaryNone);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
mesh->SetFVarInterpolateBoundaryMethod(HbrMesh<OsdVertex>::k_InterpolateBoundaryEdgeAndCorner);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mesh->SetFVarInterpolateBoundaryMethod(HbrMesh<OsdVertex>::k_InterpolateBoundaryEdgeOnly);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
mesh->SetFVarInterpolateBoundaryMethod(HbrMesh<OsdVertex>::k_InterpolateBoundaryAlwaysSharp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*XXX
|
||||||
|
TF_WARN("Subdivmesh contains unknown facevarying interpolate "
|
||||||
|
"boundary method: %d.\n", currentInt[0]);
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if(strcmp(tag, "smoothtriangles") == 0) {
|
||||||
|
// Do nothing - CatmarkMesh should handle it
|
||||||
|
} else if(strcmp(tag, "creasemethod") == 0) {
|
||||||
|
if(nstring < 1) {
|
||||||
|
/*XXX
|
||||||
|
TF_WARN("Creasemethod tag missing string argument on SubdivisionMesh.\n");
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
HbrSubdivision<OsdVertex>* subdivisionMethod = mesh->GetSubdivision();
|
||||||
|
if(strcmp(currentString->c_str(), "normal") == 0) {
|
||||||
|
subdivisionMethod->SetCreaseSubdivisionMethod(
|
||||||
|
HbrSubdivision<OsdVertex>::k_CreaseNormal);
|
||||||
|
} else if(strcmp(currentString->c_str(), "chaikin") == 0) {
|
||||||
|
subdivisionMethod->SetCreaseSubdivisionMethod(
|
||||||
|
HbrSubdivision<OsdVertex>::k_CreaseChaikin);
|
||||||
|
} else {
|
||||||
|
/*XXX
|
||||||
|
TF_WARN("Creasemethod tag specifies unknown crease "
|
||||||
|
"subdivision method '%s' on SubdivisionMesh.\n",
|
||||||
|
currentString->c_str());
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(strcmp(tag, "facevaryingpropagatecorners") == 0) {
|
||||||
|
if(nint != 1) {
|
||||||
|
/*XXX
|
||||||
|
TF_WARN("Expecting single integer argument for "
|
||||||
|
"\"facevaryingpropagatecorners\" on SubdivisionMesh.\n");
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
mesh->SetFVarPropagateCorners(currentInt[0] != 0);
|
||||||
|
}
|
||||||
|
} else if(strcmp(tag, "vertexedit") == 0
|
||||||
|
|| strcmp(tag, "edgeedit") == 0) {
|
||||||
|
// XXX DO EDITS
|
||||||
|
/*XXX
|
||||||
|
TF_WARN("vertexedit and edgeedit not yet supported.\n");
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
/*XXX
|
||||||
|
// Complain
|
||||||
|
TF_WARN("Unknown tag: %s.\n", tag);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the tag data pointers
|
||||||
|
currentInt += nint;
|
||||||
|
currentFloat += nfloat;
|
||||||
|
currentString += nstring;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Finishing mesh\n";
|
||||||
|
mesh->Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Interleave the face-varying sets specified by "names", adding
|
||||||
|
// floats into the "fvdata" vector. The number of added floats is:
|
||||||
|
// names.size() * NumRefinedFaces * 4
|
||||||
|
void
|
||||||
|
PxOsdUtilMesh::GetRefinedFVData(
|
||||||
|
int level, const vector<string>& names, vector<float>* outdata)
|
||||||
|
{
|
||||||
|
// First some sanity checking.
|
||||||
|
if (!outdata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i=0; i<(int)names.size(); ++i) {
|
||||||
|
const string &name = names[i];
|
||||||
|
if (_fvaroffsets.find(name) == _fvaroffsets.end()) {
|
||||||
|
/* XXX
|
||||||
|
printf("Can't find facevarying variable %s\n", name.c_str());
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch *all* faces; this includes all subdivision levels.
|
||||||
|
vector<HbrFace<OsdVertex> *> faces;
|
||||||
|
_hmesh->GetFaces(std::back_inserter(faces));
|
||||||
|
|
||||||
|
// Iterate through all faces, filtering on the requested subdivision level.
|
||||||
|
for (int i=0; i<(int)faces.size(); ++i) {
|
||||||
|
HbrFace<OsdVertex>* face = faces[i];
|
||||||
|
if (face->GetDepth() != level) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int ncorners = face->GetNumVertices();
|
||||||
|
for (int corner = 0; corner < ncorners; ++corner) {
|
||||||
|
HbrFVarData<OsdVertex>& fvariable = face->GetFVarData(corner);
|
||||||
|
|
||||||
|
for (int j=0; j<(int)names.size(); ++j) {
|
||||||
|
const string &name = names[j];
|
||||||
|
|
||||||
|
int offset = _fvaroffsets[name];
|
||||||
|
const float* data = fvariable.GetData(offset);
|
||||||
|
outdata->push_back(*data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
126
opensubdiv/osdutil/mesh.h
Normal file
126
opensubdiv/osdutil/mesh.h
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#ifndef PX_OSD_UTIL_MESH_H
|
||||||
|
#define PX_OSD_UTIL_MESH_H
|
||||||
|
|
||||||
|
#include <hbr/mesh.h>
|
||||||
|
#include <osd/vertex.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
|
// A value struct that holds annotations on a subdivision surface
|
||||||
|
// such as creases, boundaries, holes, corners, hierarchical edits, etc.
|
||||||
|
//
|
||||||
|
// For OpenSubdiv documentation on tags, see:
|
||||||
|
// See http://graphics.pixar.com/opensubdiv/docs/subdivision_surfaces.html#hierarchical-edits
|
||||||
|
//
|
||||||
|
struct PxOsdUtilTagData {
|
||||||
|
std::vector<std::string> tags;
|
||||||
|
std::vector<int> numArgs;
|
||||||
|
std::vector<int> intArgs;
|
||||||
|
std::vector<float> floatArgs;
|
||||||
|
std::vector<std::string> stringArgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A value struct intended to hold within it topology for the base mesh
|
||||||
|
// of a subdivision surface, and any annotation tags.
|
||||||
|
// It is used to initialize classes that create and operate on subdivs.
|
||||||
|
//
|
||||||
|
class PxOsdUtilSubdivTopology {
|
||||||
|
public:
|
||||||
|
|
||||||
|
PxOsdUtilSubdivTopology();
|
||||||
|
~PxOsdUtilSubdivTopology();
|
||||||
|
|
||||||
|
// XXX Would be great for these members to be private with accessors
|
||||||
|
std::string name;
|
||||||
|
int numVertices;
|
||||||
|
int maxLevels;
|
||||||
|
std::vector<int> indices;
|
||||||
|
std::vector<int> nverts;
|
||||||
|
std::vector<std::string> vvNames;
|
||||||
|
std::vector<std::string> fvNames;
|
||||||
|
std::vector<float> fvData;
|
||||||
|
PxOsdUtilTagData tagData;
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize using raw types.
|
||||||
|
//
|
||||||
|
// This is useful for automated tests initializing with data like:
|
||||||
|
// int nverts[] = { 4, 4, 4, 4, 4, 4};
|
||||||
|
//
|
||||||
|
bool Initialize(
|
||||||
|
int numVertices,
|
||||||
|
const int *nverts, int numFaces,
|
||||||
|
const int *indices, int indicesLen,
|
||||||
|
int levels,
|
||||||
|
std::string *errorMessage);
|
||||||
|
|
||||||
|
// checks indices etc to ensure that mesh isn't in a
|
||||||
|
// broken state. Returns false on error, and will populate
|
||||||
|
// errorMessage (if non-NULL) with a descriptive error message
|
||||||
|
bool IsValid(std::string *errorMessage = NULL) const;
|
||||||
|
|
||||||
|
// for debugging, print the contents of the topology to stdout
|
||||||
|
void Print() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// This class is reponsible for taking a topological description of a mesh
|
||||||
|
// defined by PxOsdUtilSubdivTopology and turn that into a halfedge mesh
|
||||||
|
// with detailed connectivity information for mesh traversal. A PxOsdUtilMesh
|
||||||
|
// object is used for uniform and feature adaptive refinement of subdivision
|
||||||
|
// surfaces (subdivs), which is itself a requirement for fast run-time
|
||||||
|
// evaluation of subdivs.
|
||||||
|
//
|
||||||
|
class PxOsdUtilMesh {
|
||||||
|
public:
|
||||||
|
|
||||||
|
PxOsdUtilMesh(
|
||||||
|
const PxOsdUtilSubdivTopology &topology,
|
||||||
|
std::string *errorMessage = NULL);
|
||||||
|
|
||||||
|
~PxOsdUtilMesh();
|
||||||
|
|
||||||
|
// Fetch the face varying attribute values on refined quads
|
||||||
|
// Traverse the hbrMesh gathering face varying data created
|
||||||
|
// by a refiner.
|
||||||
|
// XXX: this assumes uniform subdivision, should be moved
|
||||||
|
// into uniformRefiner?
|
||||||
|
void GetRefinedFVData(int subdivisionLevel,
|
||||||
|
const std::vector<std::string>& names,
|
||||||
|
std::vector<float>* fvdata);
|
||||||
|
|
||||||
|
OpenSubdiv::HbrMesh<OpenSubdiv::OsdVertex> *GetHbrMesh() { return _hmesh;}
|
||||||
|
|
||||||
|
bool IsValid() { return _valid;}
|
||||||
|
|
||||||
|
const std::string &GetName() { return _name;}
|
||||||
|
|
||||||
|
const PxOsdUtilSubdivTopology &GetTopology() const {return _t;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const PxOsdUtilSubdivTopology &_t;
|
||||||
|
|
||||||
|
std::vector<int> _fvarwidths;
|
||||||
|
std::vector<int> _fvarindices;
|
||||||
|
std::map<std::string, int> _fvaroffsets;
|
||||||
|
|
||||||
|
OpenSubdiv::HbrMesh<OpenSubdiv::OsdVertex> *_hmesh;
|
||||||
|
|
||||||
|
std::string _name;
|
||||||
|
|
||||||
|
bool _valid;
|
||||||
|
|
||||||
|
static void _ProcessTagsAndFinishMesh(
|
||||||
|
OpenSubdiv::HbrMesh<OpenSubdiv::OsdVertex> *mesh,
|
||||||
|
const std::vector<std::string> &tags,
|
||||||
|
const std::vector<int> &numArgs,
|
||||||
|
const std::vector<int> &intArgs,
|
||||||
|
const std::vector<float> &floatArgs,
|
||||||
|
const std::vector<std::string> &stringArgs);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* PX_OSD_UTIL_MESH_H */
|
301
opensubdiv/osdutil/refiner.cpp
Normal file
301
opensubdiv/osdutil/refiner.cpp
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
|
||||||
|
#include <far/meshFactory.h>
|
||||||
|
|
||||||
|
#include "refiner.h"
|
||||||
|
|
||||||
|
#include <osd/vertex.h>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
using namespace OpenSubdiv;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// The simplest constructor, only point positions and polygonal
|
||||||
|
// mesh topology
|
||||||
|
|
||||||
|
|
||||||
|
PxOsdUtilRefiner::PxOsdUtilRefiner():
|
||||||
|
_adaptive(false),
|
||||||
|
_mesh(NULL),
|
||||||
|
_farMesh(NULL),
|
||||||
|
_patchParamTable(NULL),
|
||||||
|
_firstVertexOffset(0),
|
||||||
|
_numRefinedVerts(0),
|
||||||
|
_numUniformQuads(0),
|
||||||
|
_numPatches(0),
|
||||||
|
_level(1),
|
||||||
|
_isRefined(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
PxOsdUtilRefiner::Initialize(
|
||||||
|
const PxOsdUtilSubdivTopology &topology,
|
||||||
|
bool adaptive,
|
||||||
|
string *errorMessage)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::cout << "Initializing refiner\n";
|
||||||
|
|
||||||
|
if (not topology.IsValid(errorMessage)) {
|
||||||
|
std::cout << "Topology invalid:\n\t" << *errorMessage << "\n";
|
||||||
|
topology.Print();
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
std::cout << "Topology is valid\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_mesh = new PxOsdUtilMesh(topology, errorMessage);
|
||||||
|
|
||||||
|
std::cout << "\tCreated _mesh in refiner\n";
|
||||||
|
|
||||||
|
if (not _mesh->IsValid()) {
|
||||||
|
std::cout << "Invalid mesh\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PxOsdUtilSubdivTopology &t = _mesh->GetTopology();
|
||||||
|
|
||||||
|
if (adaptive) {
|
||||||
|
std::cout << "\tAdaptive mesh for refiner\n";
|
||||||
|
FarMeshFactory<OsdVertex> adaptiveMeshFactory(
|
||||||
|
_mesh->GetHbrMesh(), t.maxLevels, true);
|
||||||
|
|
||||||
|
_farMesh = adaptiveMeshFactory.Create();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
std::cout << "\tUniform mesh for refiner, maxLevels = " << t.maxLevels << "\n";
|
||||||
|
|
||||||
|
HbrMesh<OsdVertex> *hmesh = _mesh->GetHbrMesh();
|
||||||
|
|
||||||
|
t.Print();
|
||||||
|
|
||||||
|
std::cout << "\tHbr mesh has faces " << hmesh->GetNumFaces() << " " << hmesh->GetNumCoarseFaces() << " and vertices " <<
|
||||||
|
hmesh->GetNumVertices() << ", disconnected = " <<
|
||||||
|
hmesh->GetNumDisconnectedVertices() << "\n";
|
||||||
|
|
||||||
|
hmesh->PrintStats(std::cout);
|
||||||
|
|
||||||
|
|
||||||
|
// create the quad tables to include all levels by specifying
|
||||||
|
// firstLevel as 1
|
||||||
|
FarMeshFactory<OsdVertex> uniformMeshFactory(
|
||||||
|
_mesh->GetHbrMesh(), t.maxLevels, false, /*firstLevel=*/1);
|
||||||
|
|
||||||
|
_farMesh = uniformMeshFactory.Create();
|
||||||
|
|
||||||
|
std::cout << "\tUniform farmesh created with " << t.maxLevels << "\n";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now that we've created table driven subdivision data structures
|
||||||
|
// needed for refinement, grab and cache specific values for
|
||||||
|
// later fast lookup.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Subdivision tables describe the addition steps with coefficients
|
||||||
|
// needed to perform subdivision
|
||||||
|
const FarSubdivisionTables<OsdVertex>* ftable =
|
||||||
|
_farMesh->GetSubdivisionTables();
|
||||||
|
|
||||||
|
// Find quads array at _level
|
||||||
|
const FarPatchTables * ptables = _farMesh->GetPatchTables();
|
||||||
|
const FarPatchTables::PatchArrayVector & parrays =
|
||||||
|
ptables->GetPatchArrayVector();
|
||||||
|
|
||||||
|
if (_level > (int)parrays.size()) {
|
||||||
|
/*XXX
|
||||||
|
*errorMessage = TfStringPrintf(
|
||||||
|
"Invalid size of patch array %d %d\n",
|
||||||
|
_level, (int)parrays.size());;
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parrays doesn't contain base mesh, so it starts with level==1
|
||||||
|
const FarPatchTables::PatchArray & parray = parrays[_level-1];
|
||||||
|
|
||||||
|
_patchParamTable = &(ptables->GetPatchParamTable());
|
||||||
|
|
||||||
|
// Global index of the first point in this array
|
||||||
|
_firstVertexOffset = ftable->GetFirstVertexOffset(_level);
|
||||||
|
|
||||||
|
// Global index of the first face (patch) in this array
|
||||||
|
_firstPatchOffset = parray.GetPatchIndex();
|
||||||
|
|
||||||
|
_numRefinedVerts = (int) ftable->GetNumVertices(_level);
|
||||||
|
|
||||||
|
std::cout << "refiner has " << _numRefinedVerts << " refined verts\n";
|
||||||
|
if (adaptive) {
|
||||||
|
_numPatches = (int) parray.GetNumPatches();
|
||||||
|
} else {
|
||||||
|
_numUniformQuads = (int) parray.GetNumPatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
_isRefined = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PxOsdUtilRefiner::GetRefinedQuads(
|
||||||
|
vector<int>* quads,
|
||||||
|
string *errorMessage) const
|
||||||
|
{
|
||||||
|
if (!_isRefined) {
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = "GetQuads: Mesh has not been refined.";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_adaptive) {
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = "GetQuads: only supports uniform subdivision.";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quads || (_numUniformQuads == 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
quads->resize(_numUniformQuads * 4);
|
||||||
|
|
||||||
|
const FarPatchTables * ptables = _farMesh->GetPatchTables();
|
||||||
|
const unsigned int *quadIndices = ptables->GetFaceVertices(_level);
|
||||||
|
|
||||||
|
for (int i=0; i<_numUniformQuads*4; ++i) {
|
||||||
|
(*quads)[i] = quadIndices[i] - _firstVertexOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inverse of OpenSubdiv::FarPatchParam::BitField::Normalize
|
||||||
|
static void
|
||||||
|
_InverseNormalize(OpenSubdiv::FarPatchParam::BitField bf, float& u, float& v)
|
||||||
|
{
|
||||||
|
float frac = bf.GetParamFraction();
|
||||||
|
float pu = (float) bf.GetU() * frac;
|
||||||
|
float pv = (float) bf.GetV() * frac;
|
||||||
|
|
||||||
|
u = u * frac + pu;
|
||||||
|
v = v * frac + pv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inverse of OpenSubdiv::FarPatchParam::BitField::Rotate
|
||||||
|
static void
|
||||||
|
_InverseRotate(OpenSubdiv::FarPatchParam::BitField bf, float& u, float& v)
|
||||||
|
{
|
||||||
|
switch (bf.GetRotation()) {
|
||||||
|
case 0 : break;
|
||||||
|
case 1 : { float tmp=u; u=1.0f-v; v=tmp; } break;
|
||||||
|
case 2 : { u=1.0f-u; v=1.0f-v; } break;
|
||||||
|
case 3 : { float tmp=v; v=1.0f-u; u=tmp; } break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PxOsdUtilRefiner::GetRefinedPtexUvs(vector<float>* subfaceUvs,
|
||||||
|
vector<int>* ptexIndices,
|
||||||
|
string *errorMessage) const
|
||||||
|
{
|
||||||
|
if (!_isRefined) {
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = "GetRefinedPtexUvs: Mesh has not been refined.";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_adaptive) {
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = "GetRefinedPtexUvs: only supports uniform subdivision.";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
subfaceUvs->resize(_numUniformQuads * 4);
|
||||||
|
vector<float>::iterator uvIt = subfaceUvs->begin();
|
||||||
|
|
||||||
|
ptexIndices->resize(_numUniformQuads);
|
||||||
|
vector<int>::iterator idIt = ptexIndices->begin();
|
||||||
|
|
||||||
|
const FarPatchTables * ptables = _farMesh->GetPatchTables();
|
||||||
|
const FarPatchTables::PatchArrayVector & parrays =
|
||||||
|
ptables->GetPatchArrayVector();
|
||||||
|
if (_level > (int)parrays.size()) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage = "Invalid size of patch array";
|
||||||
|
}
|
||||||
|
const FarPatchTables::PatchArray & parray = parrays[_level-1];
|
||||||
|
|
||||||
|
const FarPatchTables::PatchParamTable& paramTable =
|
||||||
|
ptables->GetPatchParamTable();
|
||||||
|
|
||||||
|
for (int refinedIndex = 0; refinedIndex < _numUniformQuads;
|
||||||
|
++refinedIndex) {
|
||||||
|
|
||||||
|
const OpenSubdiv::FarPatchParam& param =
|
||||||
|
paramTable[parray.GetPatchIndex() + refinedIndex];
|
||||||
|
OpenSubdiv::FarPatchParam::BitField bf = param.bitField;
|
||||||
|
|
||||||
|
float u0 = 0;
|
||||||
|
float v0 = 0;
|
||||||
|
_InverseRotate(bf, u0, v0);
|
||||||
|
_InverseNormalize(bf, u0, v0);
|
||||||
|
|
||||||
|
float u1 = 1;
|
||||||
|
float v1 = 1;
|
||||||
|
_InverseRotate(bf, u1, v1);
|
||||||
|
_InverseNormalize(bf, u1, v1);
|
||||||
|
|
||||||
|
*idIt++ = param.faceIndex;
|
||||||
|
*uvIt++ = u0;
|
||||||
|
*uvIt++ = v0;
|
||||||
|
*uvIt++ = u1;
|
||||||
|
*uvIt++ = v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PxOsdUtilRefiner::~PxOsdUtilRefiner() {
|
||||||
|
|
||||||
|
if (_mesh)
|
||||||
|
delete _mesh;
|
||||||
|
|
||||||
|
if (_farMesh)
|
||||||
|
delete _farMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const std::string &
|
||||||
|
PxOsdUtilRefiner::GetName()
|
||||||
|
{
|
||||||
|
if (_mesh) {
|
||||||
|
return _mesh->GetName();
|
||||||
|
} else {
|
||||||
|
static std::string bogus("bogus");
|
||||||
|
return bogus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenSubdiv::HbrMesh<OpenSubdiv::OsdVertex>*
|
||||||
|
PxOsdUtilRefiner::GetHbrMesh()
|
||||||
|
{
|
||||||
|
if (_mesh) {
|
||||||
|
return _mesh->GetHbrMesh();
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
128
opensubdiv/osdutil/refiner.h
Normal file
128
opensubdiv/osdutil/refiner.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#ifndef PXOSDUTIL_REFINER_H
|
||||||
|
#define PXOSDUTIL_REFINER_H
|
||||||
|
|
||||||
|
#include "mesh.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <osd/cpuVertexBuffer.h>
|
||||||
|
#include <osd/cpuComputeContext.h>
|
||||||
|
|
||||||
|
#include <far/mesh.h>
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// A simple class that wraps several OpenSubdiv classes for tessellating
|
||||||
|
// a subdivision surface into quads and extracting position and topology
|
||||||
|
// data. Single and multithreaded CPU evaluation is supported.
|
||||||
|
//
|
||||||
|
// At initialization time this class takes polygonal mesh topology as
|
||||||
|
// vectors of ints, constructs an HbrMesh from that with topology checking
|
||||||
|
// and does uniform subdivision on that to make a FarMesh.
|
||||||
|
//
|
||||||
|
// At runtime Osd vertex buffers, compute controllers, and compute contexts
|
||||||
|
// are used for fast evaluation of the surface given the FarMesh.
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
class PxOsdUtilRefiner {
|
||||||
|
public:
|
||||||
|
|
||||||
|
PxOsdUtilRefiner();
|
||||||
|
|
||||||
|
~PxOsdUtilRefiner();
|
||||||
|
|
||||||
|
// Returns false on error. If errorMessage is non-NULL it'll
|
||||||
|
// be populated upon error.
|
||||||
|
//
|
||||||
|
// If successful the HbrMesh and FarMesh will be created, and
|
||||||
|
// all variables will be populated for later calls to Refine.
|
||||||
|
//
|
||||||
|
bool Initialize(
|
||||||
|
const PxOsdUtilSubdivTopology &topology, bool adaptive,
|
||||||
|
std::string *errorMessage = NULL);
|
||||||
|
|
||||||
|
// Fetch the topology of the post-refined mesh. The "quads" vector
|
||||||
|
// will be filled with 4 ints per quad which index into a vector
|
||||||
|
// of positions.
|
||||||
|
bool GetRefinedQuads(std::vector<int>* quads,
|
||||||
|
std::string *errorMessage = NULL) const;
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch the U/V coordinates of the refined quads in the U/V space
|
||||||
|
// of their parent coarse face
|
||||||
|
bool GetRefinedPtexUvs(std::vector<float>* subfaceUvs,
|
||||||
|
std::vector<int>* ptexIndices,
|
||||||
|
std::string *errorMessage = NULL) const;
|
||||||
|
|
||||||
|
// Fetch the face varying attribute values on refined quads
|
||||||
|
// Calls through to the lower level mesh class to extract
|
||||||
|
// face varying data from hbr.
|
||||||
|
void GetRefinedFVData(int subdivisionLevel,
|
||||||
|
const std::vector<std::string>& names,
|
||||||
|
std::vector<float>* fvdata) {
|
||||||
|
_mesh->GetRefinedFVData(subdivisionLevel, names, fvdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Const access to far mesh
|
||||||
|
const OpenSubdiv::FarMesh<OpenSubdiv::OsdVertex>* GetFarMesh() {
|
||||||
|
return _farMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &GetName();
|
||||||
|
|
||||||
|
bool GetAdaptive() { return _adaptive; }
|
||||||
|
|
||||||
|
OpenSubdiv::HbrMesh<OpenSubdiv::OsdVertex> *GetHbrMesh();
|
||||||
|
|
||||||
|
const PxOsdUtilSubdivTopology &GetTopology() const {return _mesh->GetTopology();}
|
||||||
|
|
||||||
|
bool IsRefined() {return _isRefined;}
|
||||||
|
|
||||||
|
int GetNumRefinedVertices() { return _numRefinedVerts;}
|
||||||
|
int GetFirstVertexOffset() { return _firstVertexOffset;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// If true, feature adaptive refinement is used and _farMesh
|
||||||
|
// is populated with bspline and gregory patches.
|
||||||
|
// if false, uniform refinement is used by subdividing the entire
|
||||||
|
// mesh _level times.
|
||||||
|
bool _adaptive;
|
||||||
|
|
||||||
|
// The next block of member variables are the OpenSubdiv meshe
|
||||||
|
// classes used to generate the refined subdivision surface
|
||||||
|
//
|
||||||
|
|
||||||
|
// The lowest level mesh, it definies the polygonal topology
|
||||||
|
// and is used for refinement.
|
||||||
|
PxOsdUtilMesh *_mesh;
|
||||||
|
|
||||||
|
// A mesh of patches (adaptive), or quads (uniform) generated
|
||||||
|
// by performing feature adaptive or uniform subdivision on the hbrMesh.
|
||||||
|
// Uniform and adaptive each get their own far mesh as uniform and
|
||||||
|
// adaptive code in far result in very different meshes
|
||||||
|
OpenSubdiv::FarMesh<OpenSubdiv::OsdVertex>* _farMesh;
|
||||||
|
|
||||||
|
// Pointer to patch parameters stored within _farMesh.
|
||||||
|
// These describe additional per-patch information, used here to
|
||||||
|
// extract the U/V values at the corners of each tessellated quad.
|
||||||
|
/// XXX: Maybe not cache, get from far mesh each time?
|
||||||
|
const OpenSubdiv::FarPatchTables::PatchParamTable* _patchParamTable;
|
||||||
|
|
||||||
|
// Cached counts within _farMesh
|
||||||
|
/// XXX: Maybe not cache, get from far mesh each time?
|
||||||
|
int _firstVertexOffset;
|
||||||
|
int _firstPatchOffset;
|
||||||
|
|
||||||
|
int _numRefinedVerts;
|
||||||
|
|
||||||
|
int _numUniformQuads; // zero if adaptive = true
|
||||||
|
int _numPatches; // zero if adaptive = false
|
||||||
|
|
||||||
|
int _level;
|
||||||
|
|
||||||
|
bool _isRefined;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* PXOSDUTIL_REFINER_H */
|
Loading…
Reference in New Issue
Block a user