static void drawSubspace(ConvexSubspace const &subspace) { float const scale = de::max(bmapDebugSize, 1.f); float const width = (DENG_GAMEVIEW_WIDTH / 16) / scale; Face const &poly = subspace.poly(); HEdge *base = poly.hedge(); HEdge *hedge = base; do { Vector2d start = hedge->origin(); Vector2d end = hedge->twin().origin(); glBegin(GL_LINES); glVertex2f(start.x, start.y); glVertex2f(end.x, end.y); glEnd(); ddouble length = (end - start).length(); if(length > 0) { Vector2d const unit = (end - start) / length; Vector2d const normal(-unit.y, unit.x); GL_BindTextureUnmanaged(GL_PrepareLSTexture(LST_DYNAMIC)); glEnable(GL_TEXTURE_2D); GL_BlendMode(BM_ADD); glBegin(GL_QUADS); glTexCoord2f(0.75f, 0.5f); glVertex2f(start.x, start.y); glTexCoord2f(0.75f, 0.5f); glVertex2f(end.x, end.y); glTexCoord2f(0.75f, 1); glVertex2f(end.x - normal.x * width, end.y - normal.y * width); glTexCoord2f(0.75f, 1); glVertex2f(start.x - normal.x * width, start.y - normal.y * width); glEnd(); glDisable(GL_TEXTURE_2D); GL_BlendMode(BM_NORMAL); } // Draw a bounding box for the leaf's face geometry. start = Vector2d(poly.aaBox().minX, poly.aaBox().minY); end = Vector2d(poly.aaBox().maxX, poly.aaBox().maxY); glBegin(GL_LINES); glVertex2f(start.x, start.y); glVertex2f( end.x, start.y); glVertex2f( end.x, start.y); glVertex2f( end.x, end.y); glVertex2f( end.x, end.y); glVertex2f(start.x, end.y); glVertex2f(start.x, end.y); glVertex2f(start.x, start.y); glEnd(); } while((hedge = &hedge->next()) != base); }
/** * On which side of the half-edge does the specified @a point lie? * * @param hedge Half-edge to test. * @param point Point to test in the map coordinate space. * * @return @c <0 Point is to the left/back of the segment. * @c =0 Point lies directly on the segment. * @c >0 Point is to the right/front of the segment. */ static coord_t pointOnHEdgeSide(HEdge const &hedge, Vector2d const &point) { Vector2d const direction = hedge.twin().origin() - hedge.origin(); ddouble pointV1[2] = { point.x, point.y }; ddouble fromOriginV1[2] = { hedge.origin().x, hedge.origin().y }; ddouble directionV1[2] = { direction.x, direction.y }; return V2d_PointOnLineSide(pointV1, fromOriginV1, directionV1); }
/** * Attempt to spread the obj from the given contact from the source subspace * and into the (relative) back subsapce. * * @param subspace Convex subspace to attempt to spread over to. * * @return Always @c true. (This function is also used as an iterator.) */ void spreadInSubspace(ConvexSubspace &subspace) { HEdge *base = subspace.poly().hedge(); HEdge *hedge = base; do { maybeSpreadOverEdge(hedge); } while((hedge = &hedge->next()) != base); }
/** * @return @c true if the ray passes @a bspLeaf; otherwise @c false. */ bool crossBspLeaf(BspLeaf const &bspLeaf) { if(!bspLeaf.hasSubspace()) return false; ConvexSubspace const &subspace = bspLeaf.subspace(); // Check polyobj lines. LoopResult blocked = subspace.forAllPolyobjs([this] (Polyobj &pob) { for(Line *line : pob.lines()) { if(!crossLine(line->front())) return LoopAbort; } return LoopContinue; }); if(blocked) return false; // Check lines for the edges of the subspace geometry. HEdge *base = subspace.poly().hedge(); HEdge *hedge = base; do { if(hedge->hasMapElement()) { if(!crossLine(hedge->mapElementAs<LineSideSegment>().lineSide())) return false; } } while((hedge = &hedge->next()) != base); // Check lines for the extra meshes. blocked = subspace.forAllExtraMeshes([this] (Mesh &mesh) { for(HEdge *hedge : mesh.hedges()) { // Is this on the back of a one-sided line? if(!hedge->hasMapElement()) continue; if(!crossLine(hedge->mapElementAs<LineSideSegment>().lineSide())) return LoopAbort; } return LoopContinue; }); return !blocked; }
void buildSubspaceGeometries() { for(ConvexSubspaceProxy const &subspace : subspaces) { /// @todo Move BSP leaf construction here? BspLeaf &bspLeaf = *subspace.bspLeaf(); subspace.buildGeometry(bspLeaf, *mesh); // Account the new segments. /// @todo Refactor away. for(OrderedSegment const &oseg : subspace.segments()) { if(oseg.segment->hasHEdge()) { // There is now one more line segment. segmentCount += 1; } } } /* * Finalize the built geometry by adding a twin half-edge for any * which don't yet have one. */ for(ConvexSubspaceProxy const &convexSet : subspaces) for(OrderedSegment const &oseg : convexSet.segments()) { LineSegmentSide *seg = oseg.segment; if(seg->hasHEdge() && !seg->back().hasHEdge()) { HEdge *hedge = &seg->hedge(); DENG2_ASSERT(!hedge->hasTwin()); // Allocate the twin from the same mesh. hedge->setTwin(hedge->mesh().newHEdge(seg->back().from())); hedge->twin().setTwin(hedge); } } }
void addNeighborIntercepts(coord_t bottom, coord_t top) { ClockDirection const direction = edge? Clockwise : Anticlockwise; HEdge *hedge = wallHEdge; while((hedge = &SectorClusterCirculator::findBackNeighbor(*hedge, direction)) != wallHEdge) { // Stop if there is no back cluster. BspLeaf *backLeaf = hedge->hasFace()? &hedge->face().mapElementAs<BspLeaf>() : 0; if(!backLeaf || !backLeaf->hasCluster()) break; SectorCluster &cluster = backLeaf->cluster(); if(cluster.hasWorldVolume()) { for(int i = 0; i < cluster.visPlaneCount(); ++i) { Plane const &plane = cluster.visPlane(i); if(plane.heightSmoothed() > bottom && plane.heightSmoothed() < top) { ddouble distance = distanceTo(plane.heightSmoothed()); if(!haveEvent(distance)) { createEvent(distance); // Have we reached the div limit? if(interceptCount() == WALLEDGE_MAX_INTERCEPTS) return; } } // Clip a range bound to this height? if(plane.isSectorFloor() && plane.heightSmoothed() > bottom) bottom = plane.heightSmoothed(); else if(plane.isSectorCeiling() && plane.heightSmoothed() < top) top = plane.heightSmoothed(); // All clipped away? if(bottom >= top) return; } } else { /* * A neighbor with zero volume is a special case -- the potential * division is at the height of the back ceiling. This is because * elsewhere we automatically fix the case of a floor above a * ceiling by lowering the floor. */ coord_t z = cluster.visCeiling().heightSmoothed(); if(z > bottom && z < top) { ddouble distance = distanceTo(z); if(!haveEvent(distance)) { createEvent(distance); // All clipped away. return; } } } } }