Vec3 TriMesh::computeFaceNormal(unsigned face) const { std::vector<unsigned> fVert; getFaceVerts(face, fVert); assert(fVert.size() == 3); Vec3 p0 = getVertPos(fVert[0]); Vec3 p1 = getVertPos(fVert[1]); Vec3 p2 = getVertPos(fVert[2]); return (p1-p0).cross(p2-p1).normalized(); }
double TriMesh::computeFaceArea(unsigned face) const { std::vector<unsigned> fVert; getFaceVerts(face, fVert); assert(fVert.size() == 3); Vec3 p0 = getVertPos(fVert[0]); Vec3 p1 = getVertPos(fVert[1]); Vec3 p2 = getVertPos(fVert[2]); return Triangle::area(p0, p1, p2); }
void TriMesh::computeFaceGrad(const VectorXd& vertForm, std::vector<Vec3>& faceVector) const { faceVector.resize(numFaces()); for (unsigned face = 0; face < numFaces(); ++face) { std::vector<unsigned> fVert; getFaceVerts(face, fVert); assert(fVert.size() == 3); Vec3 pos[3]; double val[3]; for (unsigned i = 0; i < 3; ++i) { val[i] = vertForm(fVert[i]); pos[i] = getVertPos(fVert[i]); } Vec3 X = Vec3::Zero(); for (unsigned i = 0; i < 3; ++i) { unsigned j = (i + 1) % 3; unsigned k = (i + 2) % 3; X += val[i] * (pos[k] - pos[j]); } Vec3 n = (pos[1]-pos[0]).cross(pos[2]-pos[1]); double twice_area = n.norm(); n /= twice_area; faceVector[face] = n.cross(X) / twice_area; } }
void VboCircleRenderer::noFillCircle(float x, float y, float size) { int res = getResolution(size); float oneStep = M_2XPI / res; int baseIndex = noFillCounter.vertex; float rad = 0; ofVec2f pos; for(int i = 0; i < res; i++) { rad = oneStep * i; pos = getVertPos(x, y, rad, size); addVertex(noFill, pos); //put into base index //This Vertex addIndex(noFill, baseIndex + i); addColor(noFill, color); //Second if(i == (res - 1)) { addIndex(noFill, baseIndex); addColor(noFill, color); } else { addIndex(noFill, baseIndex + i + 1); addColor(noFill, color); } } }
void TriMesh::computeDivergence(const std::vector<Vec3>& faceVector, VectorXd& vertForm) const { vertForm = VectorXd::Zero(numVerts()); for (unsigned face = 0; face < numFaces(); ++face) { std::vector<unsigned> fVert; getFaceVerts(face, fVert); assert(fVert.size() == 3); Vec3 n = computeFaceNormal(face); for (unsigned i = 0; i < 3; ++i) { Vec3 p1 = getVertPos(fVert[(i+1)%3]); Vec3 p2 = getVertPos(fVert[(i+2)%3]); vertForm[fVert[i]] += 0.5*faceVector[face].dot(n.cross(p2-p1)); } } }
double TriMesh::computeFaceCotan(unsigned face, unsigned vertInFace) const { std::vector<unsigned> fVert; getFaceVerts(face, fVert); assert(fVert.size() == 3); Vec3 p[3]; for (unsigned i = 0; i < 3; ++i) p[i] = getVertPos(fVert[(vertInFace+i)%3]); return Triangle::cotan(p[0], p[1], p[2]); }
void VboCircleRenderer::filledCircle(float x, float y, float size) { int res = getResolution(size); float oneStep = M_2XPI / res; int baseIndex = filledCounter.vertex; //Set Center vertex ofVec2f center = ofVec2f(x * width, y * height); addVertex(filled, center); //Add Center first addColor(filled, color); float rad = 0; ofVec2f pos; for(int i = 0; i < res; i++) { rad = oneStep * i; pos = getVertPos(x, y, rad, size); addVertex(filled, pos); //cout << "Vertex :" << pos << endl; //Center Vertex addIndex(filled, baseIndex); addColor(filled, color); //First addIndex(filled, baseIndex + i + 1); addColor(filled, color); //Second if(i == (res - 1)) { addIndex(filled, baseIndex + 1); addColor(filled, color); } else { addIndex(filled, baseIndex + i + 2); addColor(filled, color); } } }
void AtlasOldMesher::writeCollision(Stream *s) { // First, do the binning. This is a bit gross but, hey, what can you do... const U32 gridSize = BIT(gAtlasColTreeDepth-1); const U32 gridCount = gridSize * gridSize; Vector<U16> bins[gridCount]; // Track the min/max for the bins. S16 binsMax[gridCount]; S16 binsMin[gridCount]; // Clear bins. for(S32 i=0; i<gridCount; i++) { binsMax[i] = S16_MIN; binsMin[i] = S16_MAX; } // Get the size of bins (we step in x/y, not in Z). Point3F binSize( mBounds.len_x() / F32(gridSize), mBounds.len_y() / F32(gridSize), mBounds.len_z()); for(S32 i=0; i<gridSize; i++) { for(S32 j=0; j<gridSize; j++) { // Figure the bounds for this bin... Box3F binBox; binBox.minExtents.x = binSize.x * i; binBox.minExtents.y = binSize.y * j; binBox.minExtents.z = mBounds.minExtents.z - 1.f; binBox.maxExtents.x = binSize.x * (i+1); binBox.maxExtents.y = binSize.y * (j+1); binBox.maxExtents.z = mBounds.maxExtents.z + 1.f; Vector<U16> &binList = bins[i * gridSize + j]; S16 &binMin = binsMin[i * gridSize + j]; S16 &binMax = binsMax[i * gridSize + j]; // Now, consider all the triangles in the mesh. Note: we assume a trilist. for(S32 v=0; v<mIndices.size(); v+=3) { // Get the verts. const Vert &a = mVerts[mIndices[v+0]]; const Vert &b = mVerts[mIndices[v+1]]; const Vert &c = mVerts[mIndices[v+2]]; // If it's a special, skip it, we don't want to collide with skirts. if(a.special || b.special || c.special) continue; // I can't stand skirts! // Reject anything degenerate... if(mIndices[v+0] == mIndices[v+1]) continue; if(mIndices[v+1] == mIndices[v+2]) continue; if(mIndices[v+2] == mIndices[v+0]) continue; // Otherwise, we're good, so consider it for the current bin. const Point3F aPos = getVertPos(a); const Point3F bPos = getVertPos(b); const Point3F cPos = getVertPos(c); if(triBoxOverlap(binBox, aPos, bPos, cPos)) { // Got a hit, add it to the list! binList.push_back(v); // Update the min/max info. This will be TOO BIG if we have a // very large triangle! An optimal implementation will do a clip, // then update the bin. This is probably ok for the moment. S16 hA = mHeight->sample(a.pos); S16 hB = mHeight->sample(b.pos); S16 hC = mHeight->sample(c.pos); if(hA > binMax) binMax = hA; if(hB > binMax) binMax = hB; if(hC > binMax) binMax = hC; if(hA < binMin) binMin = hA; if(hB < binMin) binMin = hB; if(hC < binMin) binMin = hC; } } // Limit the triangle count to 16bits. While primary meshes support more // than that, collision meshes don't. If we don't catch that here, we'll // see raycasting issues later in Atlas. AssertISV( binList.size() <= 65536, "AtlasOldMesher::writeCollision - too many triangles! (>65536) Try again with a deeper tree" ); // Ok, we're all set for this bin... AssertFatal(binMin <= binMax, "AtlasOldMesher::writeCollision - empty bin, crap!"); } } // Next, generate the quadtree. FrameAllocatorMarker qtPool; const U32 nodeCount = QuadTreeTracer::getNodeCount(gAtlasColTreeDepth); S16 *qtMin = (S16*)qtPool.alloc(sizeof(S16) * nodeCount); S16 *qtMax = (S16*)qtPool.alloc(sizeof(S16) * nodeCount); // We have to recursively generate this from the bins on up. First we copy // the bins from earlier, then we do our recursomatic thingummy. (It's // actually not recursive.) for(S32 i=0; i<gridSize; i++) { for(S32 j=0; j<gridSize; j++) { const U32 qtIdx = QuadTreeTracer::getNodeIndex(gAtlasColTreeDepth-1, Point2I(i,j)); qtMin[qtIdx] = binsMin[i * gridSize + j]; qtMax[qtIdx] = binsMax[i * gridSize + j]; AssertFatal(qtMin[qtIdx] <= qtMax[qtIdx], "AtlasOldMesher::writeCollision - bad child quadtree node min/max! (negative a)"); } } // Alright, now we go up the bins, generating from the four children of each, // till we hit the root. // For each empty level from bottom to top... for(S32 depth = gAtlasColTreeDepth - 2; depth >= 0; depth--) { // For each square... for(S32 i=0; i<BIT(depth); i++) for(S32 j=0; j<BIT(depth); j++) { const U32 curIdx = QuadTreeTracer::getNodeIndex(depth, Point2I(i,j)); // For each of this square's 4 children... for(S32 subI=0; subI<2; subI++) for(S32 subJ=0; subJ<2; subJ++) { const U32 subIdx = QuadTreeTracer::getNodeIndex(depth+1, Point2I(i*2+subI,j*2+subJ)); // As is the child. AssertFatal(qtMin[subIdx] <= qtMax[subIdx], "AtlasOldMesher::writeCollision - bad child quadtree node min/max! (a)"); // Update the min and max of the parent. if(qtMin[subIdx] < qtMin[curIdx]) qtMin[curIdx] = qtMin[subIdx]; if(qtMax[subIdx] > qtMax[curIdx]) qtMax[curIdx] = qtMax[subIdx]; // Make sure we actually contain the child. AssertFatal(qtMin[subIdx] >= qtMin[curIdx], "AtlasOldMesher::writeCollision - bad quadtree child min during coltree generation!"); AssertFatal(qtMax[subIdx] <= qtMax[curIdx], "AtlasOldMesher::writeCollision - bad quadtree child max during coltree generation!"); // And that the parent is still valid. AssertFatal(qtMin[curIdx] <= qtMax[curIdx], "AtlasOldMesher::writeCollision - bad parent quadtree node min/max!"); // As is the child. AssertFatal(qtMin[subIdx] <= qtMax[subIdx], "AtlasOldMesher::writeCollision - bad child quadtree node min/max! (b)"); } } } // Wasn't that fun? Now we have a ready-to-go quadtree. // Now write the quadtree, in proper order for(S32 i=0; i<nodeCount; i++) { AssertFatal(qtMin[i] <= qtMax[i], "AtlasOldMesher::writeCollision - invalid quadtree min/max."); s->write(qtMin[i]); s->write(qtMax[i]); } s->write(U32(0xb33fd34d)); // We have to generate... // ... the list of triangle offsets for each bin. (Done above!) // ... the triangle buffer which stores the offsets for each bin. ChunkTriangleBufferGenerator ctbg(gridSize); for(S32 i=0; i<gridSize; i++) for(S32 j=0; j<gridSize; j++) ctbg.insertBinList(Point2I(i,j), bins[i * gridSize + j]); // Finally, write the data out. ctbg.write(s); }
double TriMesh::computeEdgeLength(unsigned vert0, unsigned vert1) const { Vec3 p0 = getVertPos(vert0); Vec3 p1 = getVertPos(vert1); return (p1-p0).norm(); }