//------------------------------------------------------------------------------ static int checkMesh( char const * msg, std::string const & shape, int levels, Scheme scheme, int backend ) { int result =0; printf("- %s (scheme=%d)\n", msg, scheme); xyzmesh * refmesh = simpleHbr<xyzVV>(shape.c_str(), scheme, 0); refine( refmesh, levels ); std::vector<float> coarseverts; OsdHbrMesh * hmesh = simpleHbr<OpenSubdiv::OsdVertex>(shape.c_str(), scheme, coarseverts); OpenSubdiv::FarMeshFactory<OpenSubdiv::OsdVertex> meshFactory(hmesh, levels); OpenSubdiv::FarMesh<OpenSubdiv::OsdVertex> * farmesh = meshFactory.Create(); std::vector<int> remap = meshFactory.GetRemappingTable(); switch (backend) { case kBackendCPU : result = checkMeshCPU(farmesh, coarseverts, refmesh, remap); break; case kBackendCPUGL : result = checkMeshCPUGL(farmesh, coarseverts, refmesh, remap); break; case kBackendCL : result = checkMeshCL(farmesh, coarseverts, refmesh, remap); break; } delete hmesh; return result; }
void OsdPtexMeshData::initializeMesh() { if (!_hbrmesh) return; // create far mesh OpenSubdiv::FarMeshFactory<OpenSubdiv::OsdVertex> meshFactory(_hbrmesh, _level, _adaptive); _farmesh = meshFactory.Create(true /*ptex coords*/); delete _hbrmesh; _hbrmesh = NULL; int numTotalVertices = _farmesh->GetNumVertices(); // create context and vertex buffer clearComputeContextAndVertexBuffer(); if (_kernel == kCPU) { _cpuComputeContext = OpenSubdiv::OsdCpuComputeContext::Create(_farmesh); _cpuPositionBuffer = OpenSubdiv::OsdCpuGLVertexBuffer::Create(3, numTotalVertices); if (not _adaptive) _cpuNormalBuffer = OpenSubdiv::OsdCpuGLVertexBuffer::Create(3, numTotalVertices); #ifdef OPENSUBDIV_HAS_OPENMP } else if (_kernel == kOPENMP) { _cpuComputeContext = OpenSubdiv::OsdCpuComputeContext::Create(_farmesh); _cpuPositionBuffer = OpenSubdiv::OsdCpuGLVertexBuffer::Create(3, numTotalVertices); if (not _adaptive) _cpuNormalBuffer = OpenSubdiv::OsdCpuGLVertexBuffer::Create(3, numTotalVertices); #endif #ifdef OPENSUBDIV_HAS_CUDA } else if (_kernel == kCUDA) { _cudaComputeContext = OpenSubdiv::OsdCudaComputeContext::Create(_farmesh); _cudaPositionBuffer = OpenSubdiv::OsdCudaGLVertexBuffer::Create(3, numTotalVertices); if (not _adaptive) _cudaNormalBuffer = OpenSubdiv::OsdCudaGLVertexBuffer::Create(3, numTotalVertices); #endif #ifdef OPENSUBDIV_HAS_OPENCL } else if (_kernel == kCL) { _clComputeContext = OpenSubdiv::OsdCLComputeContext::Create(_farmesh, g_clContext); _clPositionBuffer = OpenSubdiv::OsdCLGLVertexBuffer::Create(3, numTotalVertices, g_clContext); if (not _adaptive) _clNormalBuffer = OpenSubdiv::OsdCLGLVertexBuffer::Create(3, numTotalVertices, g_clContext); #endif } _needsInitializeMesh = false; // get geometry from maya mesh MFnMesh meshFn(_meshDagPath); meshFn.getPoints(_pointArray); _needsUpdate = true; }
void OpenSubdMesh::tessellate(DiagSplit *split) { if (num_ptex_faces == 0) return; const int level = 3; const bool requirefvar = false; /* convert HRB to FAR mesh */ OsdHbrMesh *hbrmesh = (OsdHbrMesh*)_hbrmesh; OsdFarMeshFactory meshFactory(hbrmesh, level, true); OsdFarMesh *farmesh = meshFactory.Create(requirefvar); int num_hbr_verts = hbrmesh->GetNumVertices(); delete hbrmesh; hbrmesh = NULL; _hbrmesh = NULL; /* refine HBR mesh with vertex coordinates */ OsdCpuComputeController *compute_controller = new OsdCpuComputeController(); OsdCpuComputeContext *compute_context = OsdCpuComputeContext::Create(farmesh); OsdCpuVertexBuffer *vbuf_base = OsdCpuVertexBuffer::Create(3, num_hbr_verts); vbuf_base->UpdateData(&positions[0], 0, num_verts); compute_controller->Refine(compute_context, farmesh->GetKernelBatches(), vbuf_base); compute_controller->Synchronize(); /* split & dice patches */ OpenSubdPatch patch(farmesh, vbuf_base); for(int f = 0; f < num_ptex_faces; f++) { patch.face_id = f; split->split_quad(&patch); } /* clean up */ delete farmesh; delete compute_controller; delete compute_context; delete vbuf_base; }
//------------------------------------------------------------------------------ static OpenSubdiv::FarMesh<OpenSubdiv::OsdVertex> * createFarMesh( const char * shape, int level, bool adaptive, Scheme scheme=kCatmark ) { checkGLErrors("create osd enter"); // generate Hbr representation from "obj" description std::vector<float> positions; OsdHbrMesh * hmesh = simpleHbr<OpenSubdiv::OsdVertex>(shape, scheme, positions, g_displayStyle == kFaceVaryingColor); size_t nModel = g_bboxes.size(); float x = nModel%g_modelCount - g_modelCount*0.5f; float y = nModel/g_modelCount - g_modelCount*0.5f; // align origins Matrix matrix; identity(matrix.value); translate(matrix.value, 3*x, 3*y, 0); g_transforms.push_back(matrix); g_positions.push_back(positions); OpenSubdiv::FarMeshFactory<OpenSubdiv::OsdVertex> meshFactory(hmesh, level, adaptive); OpenSubdiv::FarMesh<OpenSubdiv::OsdVertex> *farMesh = meshFactory.Create(true); // Hbr mesh can be deleted delete hmesh; // compute model bounding (vertex animation isn't taken into account) float min[4] = { FLT_MAX, FLT_MAX, FLT_MAX, 1}; float max[4] = {-FLT_MAX, -FLT_MAX, -FLT_MAX, 1}; for (size_t i=0; i <positions.size()/3; ++i) { float v[4] = {positions[i*3], positions[i*3+1], positions[i*3+2], 1 }; for(int j=0; j<3; ++j) { min[j] = std::min(min[j], v[j]); max[j] = std::max(max[j], v[j]); } } g_bboxes.push_back(BBox(min, max)); return farMesh; }
bool OsdMesh::Create(OsdHbrMesh *hbrMesh, int level, int kernel, int exact, std::vector<int> * remap) { if (_dispatcher) delete _dispatcher; _dispatcher = OsdKernelDispatcher::CreateKernelDispatcher(level, kernel); if (not _dispatcher) { OSD_ERROR("Unknown kernel %d\n", kernel); return false; } _level = level; _exact = exact; // create Far mesh OSD_DEBUG("Create MeshFactory\n"); FarMeshFactory<OsdVertex> meshFactory(hbrMesh, _level); _farMesh = meshFactory.Create(_dispatcher); OSD_DEBUG("PREP: NumCoarseVertex = %d\n", _farMesh->GetNumCoarseVertices()); OSD_DEBUG("PREP: NumVertex = %d\n", _farMesh->GetNumVertices()); createTables( _farMesh->GetSubdivision() ); FarVertexEditTables<OsdVertex> const *editTables = _farMesh->GetVertexEdit(); if (editTables) createEditTables( editTables ); // copy the remapping table if the client needs to remap vertex indices from // Osd to Hbr for comparison / regression purposes. if (remap) (*remap)=meshFactory.GetRemappingTable(); return true; }
// ==================================== // Compute // ==================================== // // Description: // This method computes the value of the given output plug based // on the values of the input attributes. // // Arguments: // plug - the plug to compute // data - object that provides access to the attributes for this node // MStatus OsdPolySmooth::compute( const MPlug& plug, MDataBlock& data ) { MStatus returnStatus; // Check which output attribute we have been asked to compute. If this // node doesn't know how to compute it, we must return // MS::kUnknownParameter. // if( plug == a_output ) { bool createdSubdMesh = false; int subdivisionLevel = data.inputValue(a_subdivisionLevels).asInt(); short stateH = data.inputValue(state).asShort(); if ((subdivisionLevel > 0) and (stateH !=1)) { // == Retrieve input mesh ==================================== // Get attr values MObject inMeshObj = data.inputValue(a_inputPolymesh).asMesh(); short vertBoundaryMethod = data.inputValue(a_vertBoundaryMethod).asShort(); short fvarBoundaryMethod = data.inputValue(a_fvarBoundaryMethod).asShort(); bool fvarPropCorners = data.inputValue(a_fvarPropagateCorners).asBool(); bool smoothTriangles = data.inputValue(a_smoothTriangles).asBool(); short creaseMethodVal = data.inputValue(a_creaseMethod).asShort(); // Convert attr values to OSD enums HMesh::InterpolateBoundaryMethod vertInterpBoundaryMethod = ConvertMayaBoundaryMethodShortToOsdInterpolateBoundaryMethod(vertBoundaryMethod); HMesh::InterpolateBoundaryMethod fvarInterpBoundaryMethod = ConvertMayaBoundaryMethodShortToOsdInterpolateBoundaryMethod(fvarBoundaryMethod); HCatmark::CreaseSubdivision creaseMethod = (creaseMethodVal == k_creaseMethod_chaikin) ? HCatmark::k_CreaseChaikin : HCatmark::k_CreaseNormal; HCatmark::TriangleSubdivision triangleSubdivision = smoothTriangles ? HCatmark::k_New : HCatmark::k_Normal; // == Get Mesh Functions and Iterators ========================== MFnMeshData inMeshDat(inMeshObj); MFnMesh inMeshFn(inMeshObj, &returnStatus); MCHECKERR(returnStatus, "ERROR getting inMeshFn\n"); MItMeshPolygon inMeshItPolygon(inMeshObj, &returnStatus); MCHECKERR(returnStatus, "ERROR getting inMeshItPolygon\n"); // == Convert MFnMesh to OpenSubdiv ============================= // Create the hbrMesh // Note: These fvar values only need to be kept alive through the life of the farMesh std::vector<int> fvarIndices; std::vector<int> fvarWidths; HMesh *hbrMesh = createOsdHbrFromPoly( inMeshFn, inMeshItPolygon, fvarIndices, fvarWidths); assert(hbrMesh); // Create the farMesh if successfully created the hbrMesh if (hbrMesh) { // Set Boundary methods and other hbr paramters hbrMesh->SetInterpolateBoundaryMethod( vertInterpBoundaryMethod ); hbrMesh->SetFVarInterpolateBoundaryMethod( fvarInterpBoundaryMethod ); hbrMesh->SetFVarPropagateCorners(fvarPropCorners); hbrMesh->GetSubdivision()->SetCreaseSubdivisionMethod(creaseMethod); // Set HBR Catmark Subdivision parameters HCatmark *catmarkSubdivision = dynamic_cast<HCatmark *>(hbrMesh->GetSubdivision()); if (catmarkSubdivision) { catmarkSubdivision->SetTriangleSubdivisionMethod(triangleSubdivision); } // Finalize subd calculations -- apply boundary interpolation rules and resolves singular verts, etc. // NOTE: This HAS to be called after all HBR parameters are set hbrMesh->Finish(); int ncoarseverts = hbrMesh->GetNumVertices(); // Create a FarMesh from the HBR mesh and pass into // It will be owned by the OsdMesh and deleted in the ~OsdMesh() FMeshFactory meshFactory(hbrMesh, subdivisionLevel, false); FMesh *farMesh = meshFactory.Create((hbrMesh->GetTotalFVarWidth() > 0)); // == Setup OSD Data Structures ========================= int numVertexElements = 3; // only track vertex positions int numVaryingElements = 0; // XXX Future: Revise to include varying ColorSets int numVertices = inMeshFn.numVertices(); int numFarVerts = farMesh->GetNumVertices(); static OpenSubdiv::OsdCpuComputeController computeController = OpenSubdiv::OsdCpuComputeController(); OpenSubdiv::OsdCpuComputeController::ComputeContext *computeContext = OpenSubdiv::OsdCpuComputeController::ComputeContext::Create(farMesh); OpenSubdiv::OsdCpuVertexBuffer *vertexBuffer = OpenSubdiv::OsdCpuVertexBuffer::Create(numVertexElements, numFarVerts ); OpenSubdiv::OsdCpuVertexBuffer *varyingBuffer = (numVaryingElements) ? OpenSubdiv::OsdCpuVertexBuffer::Create(numVaryingElements, numFarVerts) : NULL; // == UPDATE VERTICES (can be done after farMesh generated from topology) == float const * vertex3fArray = inMeshFn.getRawPoints(&returnStatus); vertexBuffer->UpdateData(vertex3fArray, 0, numVertices ); // Hbr dupes singular vertices during Mesh::Finish() - we need // to duplicate their positions in the vertex buffer. if (ncoarseverts > numVertices) { MIntArray polyverts; for (int i=numVertices; i<ncoarseverts; ++i) { HVertex const * v = hbrMesh->GetVertex(i); HFace const * f = v->GetIncidentEdge()->GetFace(); int vidx = -1; for (int j=0; j<f->GetNumVertices(); ++j) { if (f->GetVertex(j)==v) { vidx = j; break; } } assert(vidx>-1); inMeshFn.getPolygonVertices(f->GetID(), polyverts); int vert = polyverts[vidx]; vertexBuffer->UpdateData(&vertex3fArray[0]+vert*numVertexElements, i, 1); } } // == Delete HBR // Can now delete the hbrMesh as we will only be referencing the farMesh from this point on delete hbrMesh; hbrMesh = NULL; // == Subdivide OpenSubdiv mesh ========================== computeController.Refine(computeContext, farMesh->GetKernelBatches(), vertexBuffer, varyingBuffer); computeController.Synchronize(); // == Convert subdivided OpenSubdiv mesh to MFnMesh Data outputMesh ============= // Create New Mesh Data Object MFnMeshData newMeshData; MObject newMeshDataObj = newMeshData.create(&returnStatus); MCHECKERR(returnStatus, "ERROR creating outputData"); // Create out mesh returnStatus = convertOsdFarToMayaMeshData(farMesh, vertexBuffer, subdivisionLevel, inMeshFn, newMeshDataObj); MCHECKERR(returnStatus, "ERROR convertOsdFarToMayaMesh"); // Propagate objectGroups from inMesh to outMesh (for per-facet shading, etc) returnStatus = createSmoothMesh_objectGroups(inMeshDat, subdivisionLevel, newMeshData ); // Write to output plug MDataHandle outMeshH = data.outputValue(a_output, &returnStatus); MCHECKERR(returnStatus, "ERROR getting polygon data handle\n"); outMeshH.set(newMeshDataObj); // == Cleanup OSD ============================================ // REVISIT: Re-add these deletes delete(vertexBuffer); delete(varyingBuffer); delete(computeContext); delete(farMesh); // note that the subd mesh was created (see the section below if !createdSubdMesh) createdSubdMesh = true; } } // Pass-through inMesh to outMesh if not created the subd mesh if (!createdSubdMesh) { MDataHandle outMeshH = data.outputValue(a_output, &returnStatus); returnStatus = outMeshH.copy(data.outputValue(a_inputPolymesh, &returnStatus)); MCHECKERR(returnStatus, "ERROR getting polygon data handle\n"); } // Clean up Maya Plugs data.setClean(plug); } else { // Unhandled parameter in this compute function, so return MS::kUnknownParameter // so it is handled in a parent compute() function. return MS::kUnknownParameter; } return MS::kSuccess; }
// Here is where the real meat of the OSD setup happens. The mesh topology is // created and stored for later use. Actual subdivision happens in updateGeom // which gets called at the end of this function and on frame change. // void createOsdContext(int level) { // // Setup an OsdHbr mesh based on the desired subdivision scheme // static OpenSubdiv::HbrCatmarkSubdivision<OpenSubdiv::OsdVertex> _catmark; OsdHbrMesh *hmesh(new OsdHbrMesh(&_catmark)); // // Now that we have a mesh, we need to add verticies and define the topology. // Here, we've declared the raw vertex data in-line, for simplicity // float verts[] = { 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 }; // // The cube faces are also in-lined, here they are specified as quads // int faces[] = { 0,1,3,2, 2,3,5,4, 4,5,7,6, 6,7,1,0, 1,7,5,3, 6,0,2,4 }; // // Record the original vertex positions and add verts to the mesh. // // OsdVertex is really just a place holder, it doesn't care what the // position of the vertex is, it's just being used here as a means of // defining the mesh topology. // for (unsigned i = 0; i < sizeof(verts)/sizeof(float); i += 3) { g_orgPositions.push_back(verts[i+0]); g_orgPositions.push_back(verts[i+1]); g_orgPositions.push_back(verts[i+2]); OpenSubdiv::OsdVertex vert; hmesh->NewVertex(i/3, vert); } // // Now specify the actual mesh topology by processing the faces array // const unsigned VERTS_PER_FACE = 4; for (unsigned i = 0; i < sizeof(faces)/sizeof(int); i += VERTS_PER_FACE) { // // Do some sanity checking. It is a good idea to keep this in your // code for your personal sanity as well. // // Note that this loop is not changing the HbrMesh, it's purely validating // the topology that is about to be created below. // for (unsigned j = 0; j < VERTS_PER_FACE; j++) { OsdHbrVertex * origin = hmesh->GetVertex(faces[i+j]); OsdHbrVertex * destination = hmesh->GetVertex(faces[i+((j+1)%VERTS_PER_FACE)]); OsdHbrHalfedge * opposite = destination->GetEdge(origin); if(origin==NULL || destination==NULL) { std::cerr << " An edge was specified that connected a nonexistent vertex" << std::endl; exit(1); } if(origin == destination) { std::cerr << " An edge was specified that connected a vertex to itself" << std::endl; exit(1); } if(opposite && opposite->GetOpposite() ) { std::cerr << " A non-manifold edge incident to more than 2 faces was found" << std::endl; exit(1); } if(origin->GetEdge(destination)) { std::cerr << " An edge connecting two vertices was specified more than once." " It's likely that an incident face was flipped" << std::endl; exit(1); } } // // Now, create current face given the number of verts per face and the // face index data. // /* OsdHbrFace * face = */ hmesh->NewFace(VERTS_PER_FACE, faces+i, 0); // // If you had ptex data, you would set it here, for example // /* face->SetPtexIndex(ptexIndex) */ } // // Apply some tags to drive the subdivision algorithm. Here we set the // default boundary interpolation mode along with a corner sharpness. See // the API and the renderman spec for the full list of available operations. // hmesh->SetInterpolateBoundaryMethod( OsdHbrMesh::k_InterpolateBoundaryEdgeOnly ); OsdHbrVertex * v = hmesh->GetVertex(0); v->SetSharpness(2.7f); // // Finalize the mesh object. The Finish() call is a signal to the internals // that optimizations can be made on the mesh data. // hmesh->Finish(); // // Setup some raw vectors of data. Remember that the actual point values were // not stored in the OsdVertex, so we keep track of them here instead // g_normals.resize(g_orgPositions.size(),0.0f); calcNormals( hmesh, g_orgPositions, g_normals ); // // At this point, we no longer need the topological structure of the mesh, // so we bake it down into subdivision tables by converting the HBR mesh // into an OSD mesh. Note that this is just storing the initial subdivision // tables, which will be used later during the actual subdivision process. // // Again, no vertex positions are being stored here, the point data will be // sent to the mesh in updateGeom(). // OpenSubdiv::FarMeshFactory<OpenSubdiv::OsdVertex> meshFactory(hmesh, level); g_farmesh = meshFactory.Create(); g_osdComputeContext = OpenSubdiv::OsdCpuComputeContext::Create(g_farmesh); delete hmesh; // // Initialize draw context and vertex buffer // g_vertexBuffer = OpenSubdiv::OsdCpuGLVertexBuffer::Create(6, /* 3 floats for position, + 3 floats for normal*/ g_farmesh->GetNumVertices()); g_drawContext = OpenSubdiv::OsdGLDrawContext::Create(g_farmesh->GetPatchTables(), false); g_drawContext->UpdateVertexTexture(g_vertexBuffer); // // Setup camera positioning based on object bounds. This really has nothing // to do with OSD. // computeCenterAndSize(g_orgPositions, g_center, &g_size); // // Finally, make an explicit call to updateGeom() to force creation of the // initial buffer objects for the first draw call. // updateGeom(); // // The OsdVertexBuffer provides GL identifiers which can be bound in the // standard way. Here we setup a single VAO and enable points and normals // as attributes on the vertex buffer and set the index buffer. // GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, g_vertexBuffer->BindVBO()); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 6, 0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof (GLfloat) * 6, (float*)12); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_drawContext->GetPatchIndexBuffer()); glBindBuffer(GL_ARRAY_BUFFER, 0); }