static int drawCellSubspaces(Blockmap const &bmap, BlockmapCell const &cell, void *) { bmap.forAllInCell(cell, [] (void *object) { ConvexSubspace *sub = (ConvexSubspace *)object; if(sub->validCount() != validCount) { sub->setValidCount(validCount); drawSubspace(*sub); } return LoopContinue; }); return false; // Continue iteration. }
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); }
/** * @return @c true if the ray passes @a subspace; otherwise @c false. */ bool crossSubspace(ConvexSubspace const &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; }
/** * 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); }
static void evaluateLighting(Vector3d const &origin, ConvexSubspace &subspaceAtOrigin, coord_t distToEye, bool fullbright, Vector4f &ambientColor, duint *vLightListIdx) { if(fullbright) { ambientColor = Vector3f(1, 1, 1); *vLightListIdx = 0; } else { SectorCluster &cluster = subspaceAtOrigin.cluster(); Map &map = cluster.sector().map(); if(useBias && map.hasLightGrid()) { // Evaluate the position in the light grid. Vector4f color = map.lightGrid().evaluate(origin); // Apply light range compression. for(dint i = 0; i < 3; ++i) { color[i] += Rend_LightAdaptationDelta(color[i]); } ambientColor = color; } else { Vector4f const color = cluster.lightSourceColorfIntensity(); dfloat lightLevel = color.w; /* if(spr->type == VSPR_DECORATION) { // Wall decorations receive an additional light delta. lightLevel += R_WallAngleLightLevelDelta(line, side); } */ // Apply distance attenuation. lightLevel = Rend_AttenuateLightLevel(distToEye, lightLevel); // Add extra light. lightLevel = de::clamp(0.f, lightLevel + Rend_ExtraLightDelta(), 1.f); Rend_ApplyLightAdaptation(lightLevel); // Determine the final color. ambientColor = color * lightLevel; } Rend_ApplyTorchLight(ambientColor, distToEye); *vLightListIdx = Rend_CollectAffectingLights(origin, ambientColor, &subspaceAtOrigin); } }
void Rend_DrawFlatRadio(ConvexSubspace const &subspace) { // Disabled? if(!::rendFakeRadio || ::levelFullBright) return; // If no shadow-casting lines are linked we no work to do. if(!subspace.shadowLineCount()) return; auto const &subsec = subspace.subsector().as<world::ClientSubsector>(); // Determine the shadow properties. dfloat const shadowDark = calcShadowDarkness(subsec.lightSourceIntensity()); if(shadowDark < MIN_SHADOW_DARKNESS) return; static DrawList::Indices indices; static ShadowEdge shadowEdges[2/*left, right*/]; // Keep these around (needed often). // Can skip drawing for Planes that do not face the viewer - find the 2D vector to subspace center. auto const eyeToSubspace = Vector2f(Rend_EyeOrigin().xz() - subspace.poly().center()); // All shadow geometry uses the same texture (i.e., none) - use the same list. DrawList &shadowList = ClientApp::renderSystem().drawLists().find( #if defined (DENG_OPENGL) DrawListSpec(renderWireframe? UnlitGeom : ShadowGeom) #else DrawListSpec(ShadowGeom) #endif ); // Process all LineSides linked to this subspace as potential shadow casters. subspace.forAllShadowLines([&subsec, &shadowDark, &eyeToSubspace, &shadowList] (LineSide &side) { DENG2_ASSERT(side.hasSections() && !side.line().definesPolyobj() && side.leftHEdge()); // Process each only once per frame (we only want to draw a shadow set once). if(side.shadowVisCount() != R_FrameCount()) { side.setShadowVisCount(R_FrameCount()); // Mark processed. for (dint pln = 0; pln < subsec.visPlaneCount(); ++pln) { Plane const &plane = subsec.visPlane(pln); // Skip Planes which should not receive FakeRadio shadowing. if (!plane.receivesShadow()) continue; // Skip Planes facing away from the viewer. if (Vector3f(eyeToSubspace, Rend_EyeOrigin().y - plane.heightSmoothed()) .dot(plane.surface().normal()) >= 0) { HEdge const *hEdges[2/*left, right*/] = { side.leftHEdge(), side.leftHEdge() }; if (prepareFlatShadowEdges(shadowEdges, hEdges, pln, shadowDark)) { bool const haveFloor = plane.surface().normal()[2] > 0; // Build geometry. Store &buffer = ClientApp::renderSystem().buffer(); gl::Primitive primitive; uint vertCount = makeFlatShadowGeometry(indices, buffer, primitive, shadowEdges, shadowDark, haveFloor); // Skip drawing entirely? if (::rendFakeRadio == 2) continue; // Write the geometry. shadowList.write(buffer, indices.constData(), vertCount, primitive); } } } } return LoopContinue; }); }
void updateDecorations(Surface &suf, MaterialAnimator &matAnimator, Vector2f const &materialOrigin, Vector3d const &topLeft, Vector3d const &bottomRight, Sector *containingSector = nullptr) { Vector3d delta = bottomRight - topLeft; if(de::fequal(delta.length(), 0)) return; Material &material = matAnimator.material(); int const axis = suf.normal().maxAxis(); Vector2d sufDimensions; if(axis == 0 || axis == 1) { sufDimensions.x = std::sqrt(de::squared(delta.x) + de::squared(delta.y)); sufDimensions.y = delta.z; } else { sufDimensions.x = std::sqrt(de::squared(delta.x)); sufDimensions.y = delta.y; } if(sufDimensions.x < 0) sufDimensions.x = -sufDimensions.x; if(sufDimensions.y < 0) sufDimensions.y = -sufDimensions.y; // Generate a number of decorations. int decorIndex = 0; material.forAllDecorations([&suf, &matAnimator, &materialOrigin , &topLeft, &bottomRight, &containingSector , &delta, &axis, &sufDimensions, &decorIndex] (MaterialDecoration &decor) { Vector2i const &matDimensions = matAnimator.material().dimensions(); MaterialAnimator::Decoration const &decorSS = matAnimator.decoration(decorIndex); // Skip values must be at least one. Vector2i skip = Vector2i(decor.patternSkip().x + 1, decor.patternSkip().y + 1) .max(Vector2i(1, 1)); Vector2f repeat = matDimensions * skip; if(repeat == Vector2f(0, 0)) return LoopAbort; Vector3d origin = topLeft + suf.normal() * decorSS.elevation(); float s = de::wrap(decorSS.origin().x - matDimensions.x * decor.patternOffset().x + materialOrigin.x, 0.f, repeat.x); // Plot decorations. for(; s < sufDimensions.x; s += repeat.x) { // Determine the topmost point for this row. float t = de::wrap(decorSS.origin().y - matDimensions.y * decor.patternOffset().y + materialOrigin.y, 0.f, repeat.y); for(; t < sufDimensions.y; t += repeat.y) { float const offS = s / sufDimensions.x; float const offT = t / sufDimensions.y; Vector3d patternOffset(offS, axis == VZ? offT : offS, axis == VZ? offS : offT); Vector3d decorOrigin = origin + delta * patternOffset; ConvexSubspace *subspace = suf.map().bspLeafAt(decorOrigin).subspacePtr(); if(!subspace) continue; if(!subspace->contains(decorOrigin)) continue; if(containingSector) { // The point must be in the correct sector. if(containingSector != &subspace->sector()) continue; } suf.addDecoration(new LightDecoration(decorSS, decorOrigin)); } } decorIndex += 1; return LoopContinue; }); }