// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "scene_subdiv_mesh.h" #include "scene.h" #include "../subdiv/patch_eval.h" #include "../subdiv/patch_eval_simd.h" #include "../../common/algorithms/parallel_map.h" #include "../../common/algorithms/parallel_set.h" #include "../../common/algorithms/parallel_sort.h" #include "../../common/algorithms/parallel_prefix_sum.h" #include "../../common/algorithms/parallel_for.h" /*! maximum number of user vertex buffers for subdivision surfaces */ #define RTC_MAX_USER_VERTEX_BUFFERS 65536 namespace embree { #if defined(EMBREE_LOWEST_ISA) struct VertexCreaseMap { parallel_map vertexCreaseMap; }; struct EdgeCreaseMap { parallel_map edgeCreaseMap; }; struct HoleSet{ parallel_set holeSet; }; SubdivMesh::SubdivMesh (Device* device) : Geometry(device,GTY_SUBDIV_MESH,0,1), displFunc(nullptr), tessellationRate(2.0f), numHalfEdges(0), faceStartEdge(device,0), halfEdgeFace(device,0), holeSet(new HoleSet), invalid_face(device,0), vertexCreaseMap(new VertexCreaseMap), edgeCreaseMap(new EdgeCreaseMap), commitCounter(0) { vertices.resize(numTimeSteps); vertex_buffer_tags.resize(numTimeSteps); topology.resize(1); topology[0] = Topology(this); } SubdivMesh::~SubdivMesh() {} void SubdivMesh::addElementsToCount (GeometryCounts & counts) const { if (numTimeSteps == 1) counts.numSubdivPatches += numPrimitives; else counts.numMBSubdivPatches += numPrimitives; } void SubdivMesh::setMask (unsigned mask) { this->mask = mask; Geometry::update(); } void SubdivMesh::setSubdivisionMode (unsigned topologyID, RTCSubdivisionMode mode) { if (topologyID >= topology.size()) throw_RTCError(RTC_ERROR_INVALID_OPERATION,"invalid topology ID"); topology[topologyID].setSubdivisionMode(mode); Geometry::update(); } void SubdivMesh::setVertexAttributeTopology(unsigned int vertexAttribID, unsigned int topologyID) { if (vertexAttribID < vertexAttribs.size()){ if (topologyID < topology.size()) { if ((unsigned)vertexAttribs[vertexAttribID].userData != topologyID) { vertexAttribs[vertexAttribID].userData = topologyID; commitCounter++; // triggers recalculation of cached interpolation data } } else { throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid topology specified"); } } else { throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid vertex attribute specified"); } } void SubdivMesh::setNumTimeSteps (unsigned int numTimeSteps) { vertices.resize(numTimeSteps); vertex_buffer_tags.resize(numTimeSteps); Geometry::setNumTimeSteps(numTimeSteps); } void SubdivMesh::setVertexAttributeCount (unsigned int N) { vertexAttribs.resize(N); vertex_attrib_buffer_tags.resize(N); Geometry::update(); } void SubdivMesh::setTopologyCount (unsigned int N) { if (N == 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT,"at least one topology has to exist") size_t begin = topology.size(); topology.resize(N); for (size_t i = begin; i < topology.size(); i++) topology[i] = Topology(this); } void SubdivMesh::setBuffer(RTCBufferType type, unsigned int slot, RTCFormat format, const Ref& buffer, size_t offset, size_t stride, unsigned int num) { /* verify that all accesses are 4 bytes aligned */ if (((size_t(buffer->getPtr()) + offset) & 0x3) || (stride & 0x3)) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "data must be 4 bytes aligned"); if (type != RTC_BUFFER_TYPE_LEVEL) commitCounter++; if (type == RTC_BUFFER_TYPE_VERTEX) { if (format != RTC_FORMAT_FLOAT3) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid vertex buffer format"); if (slot >= vertices.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid vertex buffer slot"); vertices[slot].set(buffer, offset, stride, num, format); vertices[slot].checkPadding16(); } else if (type == RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE) { if (format < RTC_FORMAT_FLOAT || format > RTC_FORMAT_FLOAT16) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid vertex attribute buffer format"); if (slot >= vertexAttribs.size()) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid vertex attribute buffer slot"); vertexAttribs[slot].set(buffer, offset, stride, num, format); vertexAttribs[slot].checkPadding16(); } else if (type == RTC_BUFFER_TYPE_FACE) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); if (format != RTC_FORMAT_UINT) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid face buffer format"); faceVertices.set(buffer, offset, stride, num, format); setNumPrimitives(num); } else if (type == RTC_BUFFER_TYPE_INDEX) { if (format != RTC_FORMAT_UINT) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid face buffer format"); if (slot >= topology.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid index buffer slot"); topology[slot].vertexIndices.set(buffer, offset, stride, num, format); } else if (type == RTC_BUFFER_TYPE_EDGE_CREASE_INDEX) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); if (format != RTC_FORMAT_UINT2) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid edge crease index buffer format"); edge_creases.set(buffer, offset, stride, num, format); } else if (type == RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); if (format != RTC_FORMAT_FLOAT) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid edge crease weight buffer format"); edge_crease_weights.set(buffer, offset, stride, num, format); } else if (type == RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); if (format != RTC_FORMAT_UINT) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid vertex crease index buffer format"); vertex_creases.set(buffer, offset, stride, num, format); } else if (type == RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); if (format != RTC_FORMAT_FLOAT) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid vertex crease weight buffer format"); vertex_crease_weights.set(buffer, offset, stride, num, format); } else if (type == RTC_BUFFER_TYPE_HOLE) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); if (format != RTC_FORMAT_UINT) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid hole buffer format"); holes.set(buffer, offset, stride, num, format); } else if (type == RTC_BUFFER_TYPE_LEVEL) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); if (format != RTC_FORMAT_FLOAT) throw_RTCError(RTC_ERROR_INVALID_OPERATION, "invalid level buffer format"); levels.set(buffer, offset, stride, num, format); } else { throw_RTCError(RTC_ERROR_INVALID_ARGUMENT,"unknown buffer type"); } } void* SubdivMesh::getBuffer(RTCBufferType type, unsigned int slot) { if (type == RTC_BUFFER_TYPE_VERTEX) { if (slot >= vertices.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return vertices[slot].getPtr(); } else if (type == RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE) { if (slot >= vertexAttribs.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return vertexAttribs[slot].getPtr(); } else if (type == RTC_BUFFER_TYPE_FACE) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return faceVertices.getPtr(); } else if (type == RTC_BUFFER_TYPE_INDEX) { if (slot >= topology.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return topology[slot].vertexIndices.getPtr(); } else if (type == RTC_BUFFER_TYPE_EDGE_CREASE_INDEX) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return edge_creases.getPtr(); } else if (type == RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return edge_crease_weights.getPtr(); } else if (type == RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return vertex_creases.getPtr(); } else if (type == RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return vertex_crease_weights.getPtr(); } else if (type == RTC_BUFFER_TYPE_HOLE) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return holes.getPtr(); } else if (type == RTC_BUFFER_TYPE_LEVEL) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); return levels.getPtr(); } else { throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "unknown buffer type"); return nullptr; } } void SubdivMesh::updateBuffer(RTCBufferType type, unsigned int slot) { if (type != RTC_BUFFER_TYPE_LEVEL) commitCounter++; if (type == RTC_BUFFER_TYPE_VERTEX) { if (slot >= vertices.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); vertices[slot].setModified(); } else if (type == RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE) { if (slot >= vertexAttribs.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); vertexAttribs[slot].setModified(); } else if (type == RTC_BUFFER_TYPE_FACE) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); faceVertices.setModified(); } else if (type == RTC_BUFFER_TYPE_INDEX) { if (slot >= topology.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); topology[slot].vertexIndices.setModified(); } else if (type == RTC_BUFFER_TYPE_EDGE_CREASE_INDEX) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); edge_creases.setModified(); } else if (type == RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); edge_crease_weights.setModified(); } else if (type == RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); vertex_creases.setModified(); } else if (type == RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); vertex_crease_weights.setModified(); } else if (type == RTC_BUFFER_TYPE_HOLE) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); holes.setModified(); } else if (type == RTC_BUFFER_TYPE_LEVEL) { if (slot != 0) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid buffer slot"); levels.setModified(); } else { throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "unknown buffer type"); } Geometry::update(); } void SubdivMesh::setDisplacementFunction (RTCDisplacementFunctionN func) { this->displFunc = func; } void SubdivMesh::setTessellationRate(float N) { tessellationRate = N; levels.setModified(); } __forceinline uint64_t pair64(unsigned int x, unsigned int y) { if (xdevice,0) { } void SubdivMesh::Topology::setSubdivisionMode (RTCSubdivisionMode mode) { if (subdiv_mode == mode) return; subdiv_mode = mode; mesh->updateBuffer(RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT, 0); } void SubdivMesh::Topology::update () { vertexIndices.setModified(); } bool SubdivMesh::Topology::verify (size_t numVertices) { size_t ofs = 0; for (size_t i=0; isize(); i++) { int valence = mesh->faceVertices[i]; for (size_t j=ofs; j= vertexIndices.size()) return false; if (vertexIndices[j] >= numVertices) return false; } ofs += valence; } return true; } void SubdivMesh::Topology::calculateHalfEdges() { const size_t blockSize = 4096; const size_t numEdges = mesh->numEdges(); const size_t numFaces = mesh->numFaces(); const size_t numHalfEdges = mesh->numHalfEdges; /* allocate temporary array */ halfEdges0.resize(numEdges); halfEdges1.resize(numEdges); /* create all half edges */ parallel_for( size_t(0), numFaces, blockSize, [&](const range& r) { for (size_t f=r.begin(); ffaceVertices[f]; const unsigned e = mesh->faceStartEdge[f]; for (unsigned de=0; detopology[0].vertexIndices[e+de]; const unsigned int endVertex0 = mesh->topology[0].vertexIndices[e+de+nextOfs]; const uint64_t key0 = SubdivMesh::Edge(startVertex0,endVertex0); edge->vtx_index = startVertex; edge->next_half_edge_ofs = nextOfs; edge->prev_half_edge_ofs = prevOfs; edge->opposite_half_edge_ofs = 0; edge->edge_crease_weight = mesh->edgeCreaseMap->edgeCreaseMap.lookup(key0,0.0f); edge->vertex_crease_weight = mesh->vertexCreaseMap->vertexCreaseMap.lookup(startVertex0,0.0f); edge->edge_level = mesh->getEdgeLevel(e+de); edge->patch_type = HalfEdge::COMPLEX_PATCH; // type gets updated below edge->vertex_type = HalfEdge::REGULAR_VERTEX; if (unlikely(mesh->holeSet->holeSet.lookup(unsigned(f)))) halfEdges1[e+de] = SubdivMesh::KeyHalfEdge(std::numeric_limits::max(),edge); else halfEdges1[e+de] = SubdivMesh::KeyHalfEdge(key,edge); } } }); /* sort half edges to find adjacent edges */ radix_sort_u64(halfEdges1.data(),halfEdges0.data(),numHalfEdges); /* link all adjacent pairs of edges */ parallel_for( size_t(0), numHalfEdges, blockSize, [&](const range& r) { /* skip if start of adjacent edges was not in our range */ size_t e=r.begin(); if (e != 0 && (halfEdges1[e].key == halfEdges1[e-1].key)) { const uint64_t key = halfEdges1[e].key; while (e::max()) break; size_t N=1; while (e+Nedge_crease_weight = float(inf); } /* standard edge shared between two faces */ else if (N == 2) { /* create edge crease if winding order mismatches between neighboring patches */ if (halfEdges1[e+0].edge->next()->vtx_index != halfEdges1[e+1].edge->vtx_index) { halfEdges1[e+0].edge->edge_crease_weight = float(inf); halfEdges1[e+1].edge->edge_crease_weight = float(inf); } /* otherwise mark edges as opposites of each other */ else { halfEdges1[e+0].edge->setOpposite(halfEdges1[e+1].edge); halfEdges1[e+1].edge->setOpposite(halfEdges1[e+0].edge); } } /* non-manifold geometry is handled by keeping vertices fixed during subdivision */ else { for (size_t i=0; ivertex_crease_weight = inf; halfEdges1[e+i].edge->vertex_type = HalfEdge::NON_MANIFOLD_EDGE_VERTEX; halfEdges1[e+i].edge->edge_crease_weight = inf; halfEdges1[e+i].edge->next()->vertex_crease_weight = inf; halfEdges1[e+i].edge->next()->vertex_type = HalfEdge::NON_MANIFOLD_EDGE_VERTEX; halfEdges1[e+i].edge->next()->edge_crease_weight = inf; } } e+=N; } }); /* set subdivision mode and calculate patch types */ parallel_for( size_t(0), numFaces, blockSize, [&](const range& r) { for (size_t f=r.begin(); ffaceStartEdge[f]]; /* for vertex topology we also test if vertices are valid */ if (this == &mesh->topology[0]) { /* calculate if face is valid */ for (size_t t=0; tnumTimeSteps; t++) mesh->invalidFace(f,t) = !edge->valid(mesh->vertices[t]) || mesh->holeSet->holeSet.lookup(unsigned(f)); } /* pin some edges and vertices */ for (size_t i=0; ifaceVertices[f]; i++) { /* pin corner vertices when requested by user */ if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_CORNERS && edge[i].isCorner()) edge[i].vertex_crease_weight = float(inf); /* pin all border vertices when requested by user */ else if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_BOUNDARY && edge[i].vertexHasBorder()) edge[i].vertex_crease_weight = float(inf); /* pin all edges and vertices when requested by user */ else if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_ALL) { edge[i].edge_crease_weight = float(inf); edge[i].vertex_crease_weight = float(inf); } } /* we have to calculate patch_type last! */ HalfEdge::PatchType patch_type = edge->patchType(); for (size_t i=0; ifaceVertices[f]; i++) edge[i].patch_type = patch_type; } }); } void SubdivMesh::Topology::updateHalfEdges() { /* we always use the geometry topology to lookup creases */ mvector& halfEdgesGeom = mesh->topology[0].halfEdges; /* assume we do no longer recalculate in the future and clear these arrays */ halfEdges0.clear(); halfEdges1.clear(); /* calculate which data to update */ const bool updateEdgeCreases = mesh->topology[0].vertexIndices.isLocalModified() || mesh->edge_creases.isLocalModified() || mesh->edge_crease_weights.isLocalModified(); const bool updateVertexCreases = mesh->topology[0].vertexIndices.isLocalModified() || mesh->vertex_creases.isLocalModified() || mesh->vertex_crease_weights.isLocalModified(); const bool updateLevels = mesh->levels.isLocalModified(); /* parallel loop over all half edges */ parallel_for( size_t(0), mesh->numHalfEdges, size_t(4096), [&](const range& r) { for (size_t i=r.begin(); i!=r.end(); i++) { HalfEdge& edge = halfEdges[i]; if (updateLevels) edge.edge_level = mesh->getEdgeLevel(i); if (updateEdgeCreases) { if (edge.hasOpposite()) // leave weight at inf for borders edge.edge_crease_weight = mesh->edgeCreaseMap->edgeCreaseMap.lookup((uint64_t)halfEdgesGeom[i].getEdge(),0.0f); } /* we only use user specified vertex_crease_weight if the vertex is manifold */ if (updateVertexCreases && edge.vertex_type != HalfEdge::NON_MANIFOLD_EDGE_VERTEX) { edge.vertex_crease_weight = mesh->vertexCreaseMap->vertexCreaseMap.lookup(halfEdgesGeom[i].vtx_index,0.0f); /* pin corner vertices when requested by user */ if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_CORNERS && edge.isCorner()) edge.vertex_crease_weight = float(inf); /* pin all border vertices when requested by user */ else if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_BOUNDARY && edge.vertexHasBorder()) edge.vertex_crease_weight = float(inf); /* pin every vertex when requested by user */ else if (subdiv_mode == RTC_SUBDIVISION_MODE_PIN_ALL) { edge.edge_crease_weight = float(inf); edge.vertex_crease_weight = float(inf); } } /* update patch type */ if (updateEdgeCreases || updateVertexCreases) { edge.patch_type = edge.patchType(); } } }); } void SubdivMesh::Topology::initializeHalfEdgeStructures () { /* if vertex indices not set we ignore this topology */ if (!vertexIndices) return; /* allocate half edge array */ halfEdges.resize(mesh->numEdges()); /* check if we have to recalculate the half edges */ bool recalculate = false; recalculate |= vertexIndices.isLocalModified(); recalculate |= mesh->faceVertices.isLocalModified(); recalculate |= mesh->holes.isLocalModified(); /* check if we can simply update the half edges */ bool update = false; update |= mesh->topology[0].vertexIndices.isLocalModified(); // we use this buffer to copy creases to interpolation topologies update |= mesh->edge_creases.isLocalModified(); update |= mesh->edge_crease_weights.isLocalModified(); update |= mesh->vertex_creases.isLocalModified(); update |= mesh->vertex_crease_weights.isLocalModified(); update |= mesh->levels.isLocalModified(); /* now either recalculate or update the half edges */ if (recalculate) calculateHalfEdges(); else if (update) updateHalfEdges(); /* cleanup some state for static scenes */ /* if (mesh->scene_ == nullptr || mesh->scene_->isStaticAccel()) { halfEdges0.clear(); halfEdges1.clear(); } */ /* clear modified state of all buffers */ vertexIndices.clearLocalModified(); } void SubdivMesh::printStatistics() { size_t numBilinearFaces = 0; size_t numRegularQuadFaces = 0; size_t numIrregularQuadFaces = 0; size_t numComplexFaces = 0; for (size_t e=0, f=0; f()); /* calculate face of each half edge */ halfEdgeFace.resize(numHalfEdges); for (size_t f=0, h=0; fvertexCreaseMap.init(vertex_creases,vertex_crease_weights); /* create map with all edge creases */ if (edge_creases.isLocalModified() || edge_crease_weights.isLocalModified()) edgeCreaseMap->edgeCreaseMap.init(edge_creases,edge_crease_weights); /* create set with all holes */ if (holes.isLocalModified()) holeSet->holeSet.init(holes); /* create topology */ for (auto& t: topology) t.initializeHalfEdgeStructures(); /* create interpolation cache mapping for interpolatable meshes */ for (size_t i=0; iisStaticAccel()) { vertexCreaseMap->vertexCreaseMap.clear(); edgeCreaseMap->edgeCreaseMap.clear(); } */ /* clear modified state of all buffers */ faceVertices.clearLocalModified(); holes.clearLocalModified(); for (auto& buffer : vertices) buffer.clearLocalModified(); levels.clearLocalModified(); edge_creases.clearLocalModified(); edge_crease_weights.clearLocalModified(); vertex_creases.clearLocalModified(); vertex_crease_weights.clearLocalModified(); double t1 = getSeconds(); /* print statistics in verbose mode */ if (device->verbosity(2)) { std::cout << "half edge generation = " << 1000.0*(t1-t0) << "ms, " << 1E-6*double(numHalfEdges)/(t1-t0) << "M/s" << std::endl; printStatistics(); } } bool SubdivMesh::verify () { /*! verify consistent size of vertex arrays */ if (vertices.size() == 0) return false; for (const auto& buffer : vertices) if (buffer.size() != numVertices()) return false; /*! verify vertex indices */ if (!topology[0].verify(numVertices())) return false; for (auto& b : vertexAttribs) if (!topology[b.userData].verify(b.size())) return false; /*! verify vertices */ for (const auto& buffer : vertices) for (size_t i=0; i= numFaces()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid face"); return faceStartEdge[faceID]; } unsigned int SubdivMesh::getFace(unsigned int edgeID) { if (edgeID >= numHalfEdges) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid edge"); return halfEdgeFace[edgeID]; } unsigned int SubdivMesh::getNextHalfEdge(unsigned int edgeID) { if (edgeID >= numHalfEdges) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid half edge"); return edgeID + topology[0].halfEdges[edgeID].next_half_edge_ofs; } unsigned int SubdivMesh::getPreviousHalfEdge(unsigned int edgeID) { if (edgeID >= numHalfEdges) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid half edge"); return edgeID + topology[0].halfEdges[edgeID].prev_half_edge_ofs; } unsigned int SubdivMesh::getOppositeHalfEdge(unsigned int topologyID, unsigned int edgeID) { if (topologyID >= topology.size()) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid topology"); if (edgeID >= numHalfEdges) throw_RTCError(RTC_ERROR_INVALID_ARGUMENT, "invalid half edge"); return edgeID + topology[topologyID].halfEdges[edgeID].opposite_half_edge_ofs; } #endif namespace isa { SubdivMesh* createSubdivMesh(Device* device) { return new SubdivMeshISA(device); } void SubdivMeshISA::interpolate(const RTCInterpolateArguments* const args) { unsigned int primID = args->primID; float u = args->u; float v = args->v; RTCBufferType bufferType = args->bufferType; unsigned int bufferSlot = args->bufferSlot; float* P = args->P; float* dPdu = args->dPdu; float* dPdv = args->dPdv; float* ddPdudu = args->ddPdudu; float* ddPdvdv = args->ddPdvdv; float* ddPdudv = args->ddPdudv; unsigned int valueCount = args->valueCount; /* calculate base pointer and stride */ assert((bufferType == RTC_BUFFER_TYPE_VERTEX && bufferSlot < RTC_MAX_TIME_STEP_COUNT) || (bufferType == RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE && bufferSlot < RTC_MAX_USER_VERTEX_BUFFERS)); const char* src = nullptr; size_t stride = 0; std::vector* baseEntry = nullptr; Topology* topo = nullptr; if (bufferType == RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE) { assert(bufferSlot < vertexAttribs.size()); src = vertexAttribs[bufferSlot].getPtr(); stride = vertexAttribs[bufferSlot].getStride(); baseEntry = &vertex_attrib_buffer_tags[bufferSlot]; int topologyID = vertexAttribs[bufferSlot].userData; topo = &topology[topologyID]; } else { assert(bufferSlot < numTimeSteps); src = vertices[bufferSlot].getPtr(); stride = vertices[bufferSlot].getStride(); baseEntry = &vertex_buffer_tags[bufferSlot]; topo = &topology[0]; } bool has_P = P; bool has_dP = dPdu; assert(!has_dP || dPdv); bool has_ddP = ddPdudu; assert(!has_ddP || (ddPdvdv && ddPdudu)); for (unsigned int i=0; i(baseEntry->at(interpolationSlot(primID,i/4,stride)),commitCounter, topo->getHalfEdge(primID),src+i*sizeof(float),stride,u,v, has_P ? &Pt : nullptr, has_dP ? &dPdut : nullptr, has_dP ? &dPdvt : nullptr, has_ddP ? &ddPdudut : nullptr, has_ddP ? &ddPdvdvt : nullptr, has_ddP ? &ddPdudvt : nullptr); if (has_P) { for (size_t j=i; jvalid; const unsigned* primIDs = args->primIDs; const float* u = args->u; const float* v = args->v; unsigned int N = args->N; RTCBufferType bufferType = args->bufferType; unsigned int bufferSlot = args->bufferSlot; float* P = args->P; float* dPdu = args->dPdu; float* dPdv = args->dPdv; float* ddPdudu = args->ddPdudu; float* ddPdvdv = args->ddPdvdv; float* ddPdudv = args->ddPdudv; unsigned int valueCount = args->valueCount; /* calculate base pointer and stride */ assert((bufferType == RTC_BUFFER_TYPE_VERTEX && bufferSlot < RTC_MAX_TIME_STEP_COUNT) || (bufferType == RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE && bufferSlot < RTC_MAX_USER_VERTEX_BUFFERS)); const char* src = nullptr; size_t stride = 0; std::vector* baseEntry = nullptr; Topology* topo = nullptr; if (bufferType == RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE) { assert(bufferSlot < vertexAttribs.size()); src = vertexAttribs[bufferSlot].getPtr(); stride = vertexAttribs[bufferSlot].getStride(); baseEntry = &vertex_attrib_buffer_tags[bufferSlot]; int topologyID = vertexAttribs[bufferSlot].userData; topo = &topology[topologyID]; } else { assert(bufferSlot < numTimeSteps); src = vertices[bufferSlot].getPtr(); stride = vertices[bufferSlot].getStride(); baseEntry = &vertex_buffer_tags[bufferSlot]; topo = &topology[0]; } const int* valid = (const int*) valid_i; for (size_t i=0; i(baseEntry->at(interpolationSlot(primID,j/4,stride)),commitCounter, topo->getHalfEdge(primID),src+j*sizeof(float),stride,valid1,uu,vv, P ? P+j*N+i : nullptr, dPdu ? dPdu+j*N+i : nullptr, dPdv ? dPdv+j*N+i : nullptr, ddPdudu ? ddPdudu+j*N+i : nullptr, ddPdvdv ? ddPdvdv+j*N+i : nullptr, ddPdudv ? ddPdudv+j*N+i : nullptr, N,M); } }); } } } }