diff --git a/examples/projectTest/main.cpp b/examples/projectTest/main.cpp index 084bd32d..f8a92747 100644 --- a/examples/projectTest/main.cpp +++ b/examples/projectTest/main.cpp @@ -68,11 +68,12 @@ #include #include -#include -#include +#include #include #include "../common/stopwatch.h" +#include "../../regression/common/shape_utils.h" + #include #include @@ -86,70 +87,86 @@ using namespace OpenSubdiv; -//------------------------------------------------------------------------------ -typedef HbrMesh OsdHbrMesh; -typedef HbrVertex OsdHbrVertex; -typedef HbrFace OsdHbrFace; -typedef HbrHalfedge OsdHbrHalfedge; +static shape * readShape( char const * fname ) { -typedef FarMesh OsdFarMesh; -typedef FarMeshFactory OsdFarMeshFactory; -typedef FarSubdivisionTables OsdFarMeshSubdivision; + FILE * handle = fopen( fname, "rt" ); + if (not handle) { + printf("Could not open \"%s\" - aborting.\n", fname); + exit(0); + } + fseek( handle, 0, SEEK_END ); + size_t size = ftell(handle); + fseek( handle, 0, SEEK_SET ); + char * shapeStr = new char[size+1]; -//------------------------------------------------------------------------------ -static void -createOsdMesh(int level) + if ( fread( shapeStr, size, 1, handle)!=1 ) { + printf("Error reading \"%s\" - aborting.\n", fname); + exit(0); + } + + fclose(handle); + + shapeStr[size]='\0'; + + return shape::parseShape( shapeStr, 1 ); +} + +static bool +shapeToTopology( const shape *input, PxOsdUtilSubdivTopology *output, + std::string *errorMessage) { - float points[] = { 0.000000f, -1.414214f, 1.000000f, - 1.414214f, 0.000000f, 1.000000f, - -1.414214f, 0.000000f, 1.000000f, - 0.000000f, 1.414214f, 1.000000f, - -1.414214f, 0.000000f, -1.000000f, - 0.000000f, 1.414214f, -1.000000f, - 0.000000f, -1.414214f, -1.000000f, - 1.414214f, 0.000000f, -1.000000f }; - - 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}; + int numVertices = input->getNverts(); + output->numVertices = numVertices; + output->maxLevels = 3; //arbitrary initial value + output->nverts = input->nvertsPerFace; + output->indices = input->faceverts; -// Scheme scheme = kCatmark; + // XXX:gelder + // Need to pull over uvs and tags for better test coverage - 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; + return output->IsValid(errorMessage); +} - 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; +//------------------------------------------------------------------------------ +static bool +createOsdMesh(char *inputFile, char *outputFile, std::string *errorMessage) +{ + + shape *inputShape = readShape(inputFile); + + PxOsdUtilSubdivTopology topology; + if (not shapeToTopology(inputShape, &topology, errorMessage)) + return false; + + PxOsdUtilUniformEvaluator uniformEvaluator; + + // Create uniformEvaluator + if (not uniformEvaluator.Initialize(topology, errorMessage)) { + return false; } + // Push the vertex data + uniformEvaluator.SetCoarsePositions(inputShape->verts, errorMessage); + + // Refine with eight threads + if (not uniformEvaluator.Refine(8, errorMessage)) + return false; + std::vector refinedQuads; - if (not refiner.GetRefinedQuads(&refinedQuads, &errorMessage)) { + if (not uniformEvaluator.GetRefinedQuads(&refinedQuads, errorMessage)) { std::cout << "GetRefinedQuads failed with " << errorMessage << std::endl; } - + float *refinedPositions = NULL; + int numFloats = 0; + if (not uniformEvaluator.GetRefinedPositions(&refinedPositions, &numFloats, errorMessage)) { + std::cout << "GetRefinedPositions 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] << @@ -159,42 +176,22 @@ createOsdMesh(int level) ")\n"; } -/* - - // Push the vertex data: - std::vector pointsVec; - pointsVec.resize(sizeof(points)); - for (int i=0; i<(int)sizeof(points); ++i) { - pointsVec[i] = points[i]; + std::cout << "Hot damn, it worked.\n"; + std::cout << "Positions = " << numFloats/3 << std::endl; + for (int i=0; iSetCoarsePositions(pointsVec); - std::vector refinedPositions; +// if (not uniformEvaluator.WriteRefinedObj("foo.obj", errorMessage)) { +// std::cout << errorMessage << std::endl; +// } - 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; - } - } - -*/ + return true; } //------------------------------------------------------------------------------ @@ -207,11 +204,23 @@ callbackError(OpenSubdiv::OsdErrorType err, const char *message) //------------------------------------------------------------------------------ -int main(int, char**) { +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] < uniformMeshFactory( - _mesh->GetHbrMesh(), t.maxLevels, false, /*firstLevel=*/1); + _mesh->GetHbrMesh(), t.maxLevels, false); _farMesh = uniformMeshFactory.Create(); diff --git a/opensubdiv/osdutil/topology.cpp b/opensubdiv/osdutil/topology.cpp index c9adc9a5..bf22fee6 100644 --- a/opensubdiv/osdutil/topology.cpp +++ b/opensubdiv/osdutil/topology.cpp @@ -1,3 +1,26 @@ +// +// 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 "topology.h" #include diff --git a/opensubdiv/osdutil/topology.h b/opensubdiv/osdutil/topology.h index f4d2e1f3..7d1264aa 100644 --- a/opensubdiv/osdutil/topology.h +++ b/opensubdiv/osdutil/topology.h @@ -1,3 +1,26 @@ +// +// 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 PX_OSD_UTIL_TOPOLOGY_H #define PX_OSD_UTIL_TOPOLOGY_H diff --git a/opensubdiv/osdutil/uniformEvaluator.cpp b/opensubdiv/osdutil/uniformEvaluator.cpp new file mode 100644 index 00000000..2a7286c7 --- /dev/null +++ b/opensubdiv/osdutil/uniformEvaluator.cpp @@ -0,0 +1,319 @@ +// +// 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 "uniformEvaluator.h" + +#define HBR_ADAPTIVE +#include "../hbr/mesh.h" + +#include "../osd/vertex.h" + +#include + +#include "../osd/ompComputeController.h" +#include "../osd/cpuComputeController.h" + +#include +#include + +using namespace OpenSubdiv; +using namespace std; + + +PxOsdUtilUniformEvaluator::PxOsdUtilUniformEvaluator(): + _refiner(NULL), + _ownsRefiner(false), + _computeContext(NULL), + _vertexBuffer(NULL), + _vvBuffer(NULL) +{ +} + +PxOsdUtilUniformEvaluator::~PxOsdUtilUniformEvaluator() +{ + if (_ownsRefiner and _refiner) { + delete _refiner; + } + if (_computeContext) + delete _computeContext; + if (_vertexBuffer) + delete _vertexBuffer; + if (_vvBuffer) + delete _vvBuffer; +} + + +bool +PxOsdUtilUniformEvaluator::Initialize( + const PxOsdUtilSubdivTopology &t, + string *errorMessage) +{ + + std::cout << "Initializing evaluator with topology\n"; + + // create and initialize a refiner, passing "false" for adaptive + // to indicate we wish for uniform refinement + PxOsdUtilRefiner *refiner = new PxOsdUtilRefiner(); + _ownsRefiner = true; + + std::cout << "Created refiner\n"; + + if (not refiner->Initialize(t, false, errorMessage)) { + return false; + } + + std::cout << "Initialized refiner\n"; + + return Initialize(refiner, errorMessage); +} + +bool +PxOsdUtilUniformEvaluator::Initialize( + PxOsdUtilRefiner *refiner, + string *errorMessage) +{ + + std::cout << "Initializing evaluator with refiner\n"; + + if (refiner->GetAdaptive()) { + if (errorMessage) + *errorMessage = "Uniform evaluator requires uniform refiner"; + std::cout << *errorMessage << "\n"; + return false; + } + + // Note we assume someone else keeps this pointer alive + _refiner = refiner; + _ownsRefiner = false; + + const FarMesh *fmesh = _refiner->GetFarMesh(); + const HbrMesh *hmesh = _refiner->GetHbrMesh(); + const vector &vvNames = _refiner->GetTopology().vvNames; + + if (not (fmesh and hmesh)) { + if (errorMessage) + *errorMessage = "No valid uniform far/hbr mesh"; + std::cout << *errorMessage << "\n"; + return false; + } + + // No need to create a far mesh if no subdivision is required. + if (_refiner->GetTopology().maxLevels == 0) { + + // Three elements per unrefined point + _vertexBuffer = OsdCpuVertexBuffer::Create( + 3, hmesh->GetNumVertices()); + + // zeros + memset( _vertexBuffer->BindCpuBuffer(), 0, + 3 * hmesh->GetNumVertices() * sizeof(float)); + + if (vvNames.size()) { + + // One element in the vertex buffer for each + // named vertex varying attribute in the unrefined mesh + _vvBuffer = OsdCpuVertexBuffer::Create( + vvNames.size(), hmesh->GetNumVertices()); + + // zeros + memset( _vvBuffer->BindCpuBuffer(), 0, + vvNames.size() * hmesh->GetNumVertices() * sizeof(float)); + } + return true; + } + + _computeContext = OsdCpuComputeContext::Create(fmesh); + + // Three elements per refined point + _vertexBuffer = OsdCpuVertexBuffer::Create( + 3, fmesh->GetNumVertices()); + + // zeros + memset( _vertexBuffer->BindCpuBuffer(), 0, + 3 * fmesh->GetNumVertices() * sizeof(float)); + + + if (vvNames.size()) { + + // One element in the vertex buffer for each + // named vertex varying attribute in the refined mesh + _vvBuffer = OsdCpuVertexBuffer::Create( + vvNames.size(), fmesh->GetNumVertices()); + + // zeros + memset( _vvBuffer->BindCpuBuffer(), 0, + vvNames.size() * fmesh->GetNumVertices() * sizeof(float)); + } + + return true; +} + + +void +PxOsdUtilUniformEvaluator::SetCoarsePositions( + const vector& coords, string *errorMessage ) +{ + const float* pFloats = &coords.front(); + int numFloats = (int) coords.size(); + + //XXX: should be >= num coarse vertices + if (numFloats/3 >= _refiner->GetFarMesh()->GetNumVertices()) { + if (errorMessage) + *errorMessage = "Indexing error in tesselator"; + std::cout << *errorMessage << "\n"; + } else { + _vertexBuffer->UpdateData(pFloats, 0, numFloats / 3); + } +} + + +void +PxOsdUtilUniformEvaluator::SetCoarseVVData( + const vector& data, string *errorMessage + ) +{ + if (!_vvBuffer) { + if (!data.empty() and errorMessage) + *errorMessage = + "Mesh was not constructed with VV variables."; + return; + } + + int numElements = _vvBuffer->GetNumElements(); + int numVertices = (int) data.size() / numElements; + const float* pFloats = &data.front(); + _vvBuffer->UpdateData(pFloats, 0, numVertices); +} + + +bool +PxOsdUtilUniformEvaluator::Refine( + int numThreads, string *errorMessage) +{ + const FarMesh *fmesh = _refiner->GetFarMesh(); + + if (numThreads > 1) { + OsdOmpComputeController ompComputeController(numThreads); + ompComputeController.Refine(_computeContext, + fmesh->GetKernelBatches(), + _vertexBuffer, _vvBuffer); + } else { + OsdCpuComputeController cpuComputeController; + cpuComputeController.Refine(_computeContext, + fmesh->GetKernelBatches(), + _vertexBuffer, _vvBuffer); + } + + return true; +} + +bool +PxOsdUtilUniformEvaluator::GetRefinedPositions( + float **positions, int *numFloats, + string *errorMessage) const +{ + + if (not (positions and numFloats)) { + if (errorMessage) { + *errorMessage = + "GetRefinedPositions: positions and/or numFloats was NULL"; + } + return false; + } + + if (!_refiner->IsRefined()) { + if (errorMessage) { + *errorMessage = "GetRefinedPositions: Mesh has not been refined."; + } + return false; + } + + int numRefinedVerts = _refiner->GetNumRefinedVertices(); + int firstVertexOffset = _refiner->GetFirstVertexOffset(); + + if (numRefinedVerts == 0) { + if (errorMessage) { + *errorMessage = "GetRefinedPositions: not refined."; + } + return false; + } + + + // The vertexBuffer has all subdivision levels, here we are skipping + // past the vertices on lower subdivision levels and returning + // a pointer to the start of the most refined level + *positions = _vertexBuffer->BindCpuBuffer() + (3*firstVertexOffset); + *numFloats = numRefinedVerts*3; + + return true; +} + + +bool +PxOsdUtilUniformEvaluator::GetRefinedVVData( + float **data, int *numFloats, int *numElementsRetVal, + std::string *errorMessage ) const +{ + + + if (not (data and numFloats)) { + if (errorMessage) { + *errorMessage = + "GetRefinedVVData: data and/or numFloats was NULL"; + } + return false; + } + + if (!_refiner->IsRefined()) { + if (errorMessage) { + *errorMessage = "GetRefinedVVData: Mesh has not been refined."; + } + return false; + } + + int numRefinedVerts = _refiner->GetNumRefinedVertices(); + int firstVertexOffset = _refiner->GetFirstVertexOffset(); + + if (numRefinedVerts == 0) { + if (errorMessage) { + *errorMessage = "GetRefinedVVData: not refined."; + } + return false; + } + + + int numElements = GetTopology().vvNames.size(); + if (numElementsRetVal) + *numElementsRetVal = numElements; + + // The vertexBuffer has all subdivision levels, here we are skipping + // past the vertices on lower subdivision levels and returning + // a pointer to the start of the most refined level + *data = + _vvBuffer->BindCpuBuffer() + (numElements * firstVertexOffset); + *numFloats = numElements * numRefinedVerts; + + return true; +} + diff --git a/opensubdiv/osdutil/uniformEvaluator.h b/opensubdiv/osdutil/uniformEvaluator.h new file mode 100644 index 00000000..6803e2cd --- /dev/null +++ b/opensubdiv/osdutil/uniformEvaluator.h @@ -0,0 +1,184 @@ +// +// 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_UNIFORM_EVALUATOR_H +#define PXOSDUTIL_UNIFORM_EVALUATOR_H + +#include "refiner.h" + +#include +#include + +#define HBR_ADAPTIVE + +#include "../osd/cpuVertexBuffer.h" +#include "../osd/cpuComputeContext.h" +#include "../far/mesh.h" + +// This class takes a mesh that has undergone uniform refinement to +// a fixed subdivision level, and creates required run time OpenSubdiv +// data structures used to sample values on subdivision surfaces. +// +// An important note here is that refined positions and vertex varying +// attributes are sampled at the n-th subdivision level, not at the +// exact limit surface. Use PxOsdUtilAdaptiveEvaluator for true limits. +// +class PxOsdUtilUniformEvaluator { + public: + PxOsdUtilUniformEvaluator(); + + ~PxOsdUtilUniformEvaluator(); + + // 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 + void SetCoarsePositions( + const std::vector& coords, + std::string *errorMessage = NULL + ); + + // Set new coarse-mesh vertex varying values, need to call Refine + // before calling Get* methods + void SetCoarseVVData( + const std::vector& data, + 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); + + // Grab position results of calling Refine, return by reference a pointer + // into _vertexBuffer memory with the refined points positions, + // packed as 3 floats per point. This doesn't involve a copy, the + // pointer will be valid as long as _vertexBuffer exists. + // + bool GetRefinedPositions(float **positions, int *numFloats, + std::string *errorMessage = NULL) const; + + // Grab vertex varying results of calling Refine, return by reference + // a pointer into _vvBuffer memory with the refined data, + // packed as (numElements) floats per point. This doesn't involve + // a copy, the pointer will be valid as long as _vertexBuffer exists. + // + bool GetRefinedVVData(float **data, int *numFloats, + int *numElements = NULL, + std::string *errorMessage = NULL) const; + + // Fetch the face varying attribute values on refined quads, call + // through to the refiner but keep in evaluator API for + // one-stop-service for user API + void GetRefinedFVData(int subdivisionLevel, + const std::vector& names, + std::vector* fvdata) { + _refiner->GetRefinedFVData(subdivisionLevel, names, fvdata); + } + + + // 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. + // + // Calls through to the refiner. + // + bool GetRefinedQuads(std::vector* quads, + std::string *errorMessage = NULL) const { + return _refiner->GetRefinedQuads(quads, errorMessage); + } + + // Fetch the U/V coordinates of the refined quads in the U/V space + // of their parent coarse face + bool GetRefinedPtexUvs(std::vector* subfaceUvs, + std::vector* ptexIndices, + std::string *errorMessage = NULL) const { + return _refiner->GetRefinedPtexUvs(subfaceUvs, ptexIndices, errorMessage); + } + + + // Write the refined quad mesh to given filename, return false on error +// bool WriteRefinedObj( const std::string &filename, +// std::string *errorMessage = NULL) const; + + // 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 *GetHbrMesh() { + return _refiner->GetHbrMesh(); + } + + const PxOsdUtilSubdivTopology &GetTopology() const { + return _refiner->GetTopology(); + } + + const OpenSubdiv::FarMesh* 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; + + // responsible for performing uniform catmull/clark subdivision + // on the incoming polygonal topology. This only stores topology and + // face varying data and can be shared among threads. Doesn't store + // per-vertex information being refined in different threads, those + // are in vertex buffers that can't be shared. + OpenSubdiv::OsdCpuComputeContext *_computeContext; + OpenSubdiv::OsdCpuVertexBuffer *_vertexBuffer; + OpenSubdiv::OsdCpuVertexBuffer *_vvBuffer; +}; + + + +#endif /* PXOSDUTIL_UNIFORM_EVALUATOR_H */