45 static constexpr
Scalar kFractionUninitialized = -1.0f;
80 uint16_t umbra_index = 0u;
89 Scalar umbra_fraction = kFractionUninitialized;
94 UmbraPin* p_next =
nullptr;
95 UmbraPin* p_prev =
nullptr;
100 bool IsFractionInitialized()
const {
101 return umbra_fraction > kFractionUninitialized;
112 struct DirectionDetector {
113 Scalar last_direction_ = 0.0f;
114 size_t change_count = 0u;
118 void AccumulateDirection(
Scalar new_direction) {
119 if (last_direction_ == 0.0f || last_direction_ * new_direction < 0.0f) {
120 last_direction_ = std::copysign(1.0f, new_direction);
126 bool IsConcave()
const {
129 return change_count > 3u;
145 class UmbraPinAccumulator :
public PathTessellator::VertexWriter {
153 static constexpr
Scalar kSubPixelCount = 16.0f;
154 static constexpr
Scalar kSubPixelScale = (1.0f / kSubPixelCount);
158 enum class PathStatus {
175 explicit UmbraPinAccumulator(
Vector2 device_scale);
177 ~UmbraPinAccumulator() =
default;
181 void Reserve(
size_t vertex_count) { pins_.reserve(vertex_count); }
185 PathStatus GetStatus() {
return GetResults().status; }
189 std::vector<UmbraPin>& GetPins() {
return pins_; }
193 Point GetCentroid() {
return GetResults().centroid; }
197 Scalar GetDirection() {
return GetResults().path_direction; }
214 Scalar path_direction = 0.0f;
218 void Write(
Point point)
override;
221 void EndContour()
override;
226 const Point device_scale_;
230 std::vector<UmbraPin> pins_;
235 bool first_contour_ended_ =
false;
240 bool has_multiple_contours_ =
false;
244 std::optional<PathResults> results_;
248 PathResults& GetResults() {
249 if (results_.has_value()) {
250 return results_.value();
252 return (results_ = FinalizePath()).value();
257 PathResults FinalizePath();
270 static constexpr
Scalar GetTrigRadiusForHeight(
Scalar occluder_height) {
271 return GetPenumbraSizeForHeight(occluder_height);
276 explicit PolygonInfo(
Scalar occluder_height);
293 const std::shared_ptr<ShadowVertices> CalculateConvexShadowMesh(
296 const Tessellator::Trigs& trigs);
302 static constexpr
Scalar GetPenumbraSizeForHeight(
Scalar occluder_height) {
303 return occluder_height;
309 static constexpr
Scalar GetUmbraSizeForHeight(
Scalar occluder_height) {
310 return occluder_height;
315 static constexpr
Scalar kMinSubPixelDistanceSquared =
316 UmbraPinAccumulator::kSubPixelScale * UmbraPinAccumulator::kSubPixelScale;
319 const Scalar occluder_height_;
323 Scalar umbra_gaussian_ = 1.0f;
331 std::vector<Point> vertices_;
335 std::vector<uint16_t> indices_;
340 std::vector<Scalar> gaussians_;
345 void ComputePinDirectionsAndMinDistanceToCentroid(std::vector<UmbraPin>& pins,
346 const Point& centroid,
354 struct UmbraPinLinkedList {
355 UmbraPin* p_head_pin =
nullptr;
356 size_t pin_count = 0u;
358 bool IsNull() {
return p_head_pin ==
nullptr; }
366 UmbraPinLinkedList ResolveUmbraIntersections(std::vector<UmbraPin>& pins,
375 struct PinIntersection {
387 static std::optional<PinIntersection> ComputeIntersection(UmbraPin& pin0,
392 static constexpr
Scalar kCrossTolerance = 1.0f / 2048.0f;
393 static constexpr
Scalar kIntersectionTolerance = 1.0e-6f;
397 static constexpr
Scalar FiniteVectorLengthSquared(
Vector2 v) {
398 return !v.IsFinite() ? -1.0f : v.Dot(v);
405 static constexpr
inline bool OutsideInterval(
Scalar numer,
407 bool denom_positive) {
408 return (denom_positive && (numer < 0 || numer > denom)) ||
409 (!denom_positive && (numer > 0 || numer < denom));
421 static void RemovePin(UmbraPin* p_pin, UmbraPin** p_head);
446 void ComputeMesh(std::vector<UmbraPin>& pins,
447 const Point& centroid,
448 UmbraPinLinkedList& list,
460 void PopulateUmbraVertices(std::vector<UmbraPin>& pins,
461 UmbraPinLinkedList& list,
462 const Point centroid);
480 uint16_t AppendFan(
const UmbraPin* p_curr_pin,
481 const Point& fan_start,
482 const Point& fan_end,
483 uint16_t start_index,
489 uint16_t AppendVertex(
const Point& vertex,
Scalar gaussian);
492 void AddTriangle(uint16_t v0, uint16_t v1, uint16_t v2);
495 PolygonInfo::PolygonInfo(
Scalar occluder_height)
496 : occluder_height_(occluder_height) {}
498 const std::shared_ptr<ShadowVertices> PolygonInfo::CalculateConvexShadowMesh(
500 const Matrix& matrix,
501 const Tessellator::Trigs& trigs) {
506 Vector2 scale_2d = matrix.GetBasisScaleXY();
508 FML_DCHECK(scale_2d.x >= 0.0f && scale_2d.y >= 0.0f);
509 if (!(scale_2d.x * scale_2d.y > 0.0f)) {
511 return ShadowVertices::kEmpty;
514 UmbraPinAccumulator pin_accumulator(scale_2d);
516 Scalar scale = std::max(scale_2d.x, scale_2d.y);
517 auto [point_count, contour_count] =
519 pin_accumulator.Reserve(point_count);
521 PathTessellator::PathToFilledVertices(source, pin_accumulator, scale);
523 switch (pin_accumulator.GetStatus()) {
524 case UmbraPinAccumulator::PathStatus::kEmpty:
525 return ShadowVertices::kEmpty;
526 case UmbraPinAccumulator::PathStatus::kNonConvex:
527 case UmbraPinAccumulator::PathStatus::kMultipleContours:
529 case UmbraPinAccumulator::PathStatus::kConvex:
533 std::vector<UmbraPin>& pins = pin_accumulator.GetPins();
534 const Point& centroid = pin_accumulator.GetCentroid();
535 Scalar direction = pin_accumulator.GetDirection();
537 ComputePinDirectionsAndMinDistanceToCentroid(pins, centroid, direction);
539 UmbraPinLinkedList list = ResolveUmbraIntersections(pins, direction);
552 ComputeMesh(pins, centroid, list, trigs, direction);
556 Vector2 inverted_scale_2d = 1.0f / scale_2d;
557 for (
Point& vertex : vertices_) {
558 vertex = vertex * inverted_scale_2d;
561 return ShadowVertices::Make(std::move(vertices_), std::move(indices_),
562 std::move(gaussians_));
565 UmbraPinAccumulator::UmbraPinAccumulator(
Vector2 device_scale)
566 : device_scale_(device_scale * kSubPixelCount) {}
573 void UmbraPinAccumulator::Write(
Point point) {
575 if (first_contour_ended_) {
576 has_multiple_contours_ =
true;
579 FML_DCHECK(!has_multiple_contours_);
581 point = ToDeviceGrid(point);
583 if (!pins_.empty()) {
586 Point prev = pins_.back().path_vertex;
595 if (pins_.size() >= 2u) {
597 Point prev_prev = pins_.end()[-2].path_vertex;
599 Vector2 v1 = point - prev_prev;
600 Scalar cross = v0.Cross(v1);
607 if (point == prev_prev) {
619 pins_.emplace_back(point);
631 void UmbraPinAccumulator::EndContour() {
633 if (first_contour_ended_) {
634 has_multiple_contours_ =
true;
637 FML_DCHECK(!has_multiple_contours_);
641 FML_DCHECK(pins_.front().path_vertex == pins_.back().path_vertex);
643 first_contour_ended_ =
true;
647 Point UmbraPinAccumulator::ToDeviceGrid(
Point point) {
648 return (point * device_scale_).
Round() * kSubPixelScale;
668 UmbraPinAccumulator::PathResults UmbraPinAccumulator::FinalizePath() {
669 FML_DCHECK(!results_.has_value());
671 if (has_multiple_contours_) {
672 return {.status = PathStatus::kMultipleContours};
675 if (pins_.size() < 3u) {
676 return {.status = PathStatus::kEmpty};
679 DirectionDetector x_direction_detector;
680 DirectionDetector y_direction_detector;
682 Point relative_centroid;
683 Scalar path_direction = 0.0f;
686 Point prev = pins_.back().path_vertex;
687 Point prev_prev = pins_.end()[-2].path_vertex;
688 Point first = pins_.front().path_vertex;
689 for (UmbraPin& pin : pins_) {
690 Point new_point = pin.path_vertex;
694 Vector2 delta = new_point - prev;
695 x_direction_detector.AccumulateDirection(delta.x);
696 y_direction_detector.AccumulateDirection(delta.y);
697 if (x_direction_detector.IsConcave() ||
698 y_direction_detector.IsConcave()) {
699 return {.status = PathStatus::kNonConvex};
704 if (path_direction != 0.0f) {
706 Vector2 v1 = new_point - prev_prev;
707 Scalar cross = v0.Cross(v1);
709 FML_DCHECK(cross != 0.0f);
710 if (cross * path_direction < 0.0f) {
711 return {.status = PathStatus::kNonConvex};
718 Vector2 v1 = new_point - first;
719 Scalar quad_area = v0.Cross(v1);
720 if (quad_area != 0) {
724 if (path_direction == 0) {
725 path_direction = std::copysign(1.0f, quad_area);
726 }
else if (quad_area * path_direction < 0) {
727 return {.status = PathStatus::kNonConvex};
730 relative_centroid += (v0 + v1) * quad_area;
731 path_area += quad_area;
739 if (path_direction == 0.0f) {
741 return {.status = PathStatus::kEmpty};
781 relative_centroid /= 3.0f * path_area;
786 .status = PathStatus::kConvex,
787 .centroid = pins_[0].path_vertex + relative_centroid,
788 .path_direction = path_direction,
792 void PolygonInfo::ComputePinDirectionsAndMinDistanceToCentroid(
793 std::vector<UmbraPin>& pins,
794 const Point& centroid,
796 Scalar desired_umbra_size = GetUmbraSizeForHeight(occluder_height_);
797 Scalar min_umbra_squared = desired_umbra_size * desired_umbra_size;
798 FML_DCHECK(direction == 1.0f || direction == -1.0f);
807 UmbraPin* p_prev_pin = &pins.back();
808 for (UmbraPin& pin : pins) {
809 UmbraPin* p_curr_pin = &pin;
812 Scalar distance_squared = centroid.GetDistanceToSegmentSquared(
813 p_prev_pin->path_vertex, p_curr_pin->path_vertex);
814 min_umbra_squared = std::min(min_umbra_squared, distance_squared);
816 p_prev_pin = p_curr_pin;
819 static constexpr
auto kTolerance = 1.0e-2f;
820 Scalar umbra_size = std::sqrt(min_umbra_squared);
821 if (umbra_size < desired_umbra_size + kTolerance) {
824 auto newInset = umbra_size - kTolerance;
825 auto ratio = 0.5f * (newInset / desired_umbra_size + 1);
826 FML_DCHECK(std::isfinite(ratio));
828 umbra_gaussian_ = ratio;
829 umbra_size = newInset;
831 FML_DCHECK(umbra_gaussian_ == 1.0f);
838 Scalar penumbra_scale = -GetPenumbraSizeForHeight(occluder_height_);
839 p_prev_pin = &pins.back();
840 for (UmbraPin& pin : pins) {
841 UmbraPin* p_curr_pin = &pin;
842 p_curr_pin->p_prev = p_prev_pin;
843 p_prev_pin->p_next = p_curr_pin;
849 p_prev_pin->path_delta = p_curr_pin->path_vertex - p_prev_pin->path_vertex;
850 Vector2 pin_direction = p_prev_pin
856 p_prev_pin->penumbra_delta = pin_direction * penumbra_scale;
857 p_prev_pin->umbra_vertex =
858 p_prev_pin->pin_tip =
859 p_prev_pin->path_vertex + pin_direction * umbra_size;
861 p_prev_pin = p_curr_pin;
875 std::optional<PolygonInfo::PinIntersection> PolygonInfo::ComputeIntersection(
880 Vector2 tip_delta = pin1.pin_tip - pin0.pin_tip;
882 Scalar denom = pin0.path_delta.Cross(pin1.path_delta);
883 bool denom_positive = (denom > 0);
884 Scalar numerator0, numerator1;
911 Scalar v0_length_squared = FiniteVectorLengthSquared(v0);
912 if (v0_length_squared <= 0.0f) {
914 Scalar v1_length_squared = FiniteVectorLengthSquared(v1);
915 if (v1_length_squared <= 0.0f) {
917 if (w.IsFinite() && !w.IsZero()) {
919 .intersection = pin0.pin_tip,
929 numerator1 = v1.Dot(-w);
930 denom = v1_length_squared;
931 if (OutsideInterval(numerator1, denom,
true)) {
937 numerator0 = v0.Dot(w);
938 denom = v0_length_squared;
940 if (OutsideInterval(numerator0, denom,
true)) {
943 Scalar v1_length_squared = FiniteVectorLengthSquared(v1);
944 if (v1_length_squared <= 0.0f) {
949 Scalar old_numerator0 = numerator0;
950 numerator0 = v0.Dot(w + v1);
952 if (OutsideInterval(numerator0, denom,
true)) {
956 if (numerator0 * old_numerator0 > 0) {
961 numerator1 = v1.Dot(-w);
962 denom = v1_length_squared;
967 numerator0 = w.Cross(v1);
968 if (OutsideInterval(numerator0, denom, denom_positive)) {
971 numerator1 = w.Cross(v0);
972 if (OutsideInterval(numerator1, denom, denom_positive)) {
977 Scalar fraction0 = numerator0 / denom;
978 Scalar fraction1 = numerator1 / denom;
981 .intersection = pin0.pin_tip + v0 * fraction0,
982 .fraction0 = fraction0,
983 .fraction1 = fraction1,
987 void PolygonInfo::RemovePin(UmbraPin* p_pin, UmbraPin** p_head) {
988 UmbraPin* p_next = p_pin->p_next;
989 UmbraPin* p_prev = p_pin->p_prev;
990 p_prev->p_next = p_next;
991 p_next->p_prev = p_prev;
992 if (*p_head == p_pin) {
993 *p_head = (p_next == p_pin) ?
nullptr : p_next;
1000 int PolygonInfo::ComputeSide(
const Point& p0,
1004 Scalar cross = v.Cross(w);
1006 return ((cross > 0) ? 1 : -1);
1015 PolygonInfo::UmbraPinLinkedList PolygonInfo::ResolveUmbraIntersections(
1016 std::vector<UmbraPin>& pins,
1018 UmbraPin* p_head_pin = &pins.front();
1019 UmbraPin* p_curr_pin = p_head_pin;
1020 UmbraPin* p_prev_pin = p_curr_pin->p_prev;
1021 size_t umbra_vertex_count = pins.size();
1024 size_t allowed_iterations = pins.size() * pins.size() + 1u;
1026 while (p_head_pin && p_prev_pin != p_curr_pin) {
1027 if (--allowed_iterations == 0) {
1031 std::optional<PinIntersection> intersection =
1032 ComputeIntersection(*p_prev_pin, *p_curr_pin);
1033 if (intersection.has_value()) {
1036 if (intersection->fraction0 < p_prev_pin->umbra_fraction) {
1038 RemovePin(p_prev_pin, &p_head_pin);
1039 --umbra_vertex_count;
1041 p_prev_pin = p_prev_pin->p_prev;
1042 }
else if (p_curr_pin->IsFractionInitialized() &&
1043 p_curr_pin->umbra_vertex.GetDistanceSquared(
1044 intersection->intersection) < kIntersectionTolerance) {
1050 p_curr_pin->umbra_vertex = intersection->intersection;
1051 p_curr_pin->umbra_fraction = intersection->fraction1;
1054 p_prev_pin = p_curr_pin;
1055 p_curr_pin = p_curr_pin->p_next;
1059 int side = direction * ComputeSide(p_curr_pin->pin_tip,
1060 p_curr_pin->path_delta,
1061 p_prev_pin->pin_tip);
1063 side == direction * ComputeSide(p_curr_pin->pin_tip,
1064 p_curr_pin->path_delta,
1065 p_prev_pin->pin_tip +
1066 p_prev_pin->path_delta)) {
1068 RemovePin(p_prev_pin, &p_head_pin);
1069 --umbra_vertex_count;
1071 p_prev_pin = p_prev_pin->p_prev;
1074 RemovePin(p_curr_pin, &p_head_pin);
1075 --umbra_vertex_count;
1076 p_curr_pin = p_curr_pin->p_next;
1087 p_prev_pin = p_head_pin;
1088 p_curr_pin = p_head_pin->p_next;
1089 size_t umbra_vertices = 1u;
1090 while (p_curr_pin != p_head_pin) {
1091 if (p_prev_pin->umbra_vertex.GetDistanceSquared(p_curr_pin->umbra_vertex) <
1092 kMinSubPixelDistanceSquared) {
1093 RemovePin(p_curr_pin, &p_head_pin);
1094 p_curr_pin = p_curr_pin->p_next;
1097 p_prev_pin = p_curr_pin;
1098 p_curr_pin = p_curr_pin->p_next;
1100 FML_DCHECK(p_curr_pin == p_prev_pin->p_next);
1101 FML_DCHECK(p_prev_pin == p_curr_pin->p_prev);
1104 if (umbra_vertices < 3u) {
1108 return {p_head_pin, umbra_vertices};
1142 void PolygonInfo::ComputeMesh(std::vector<UmbraPin>& pins,
1143 const Point& centroid,
1144 UmbraPinLinkedList& list,
1148 size_t vertex_count = list.pin_count + 1u;
1149 size_t triangle_count = list.pin_count;
1152 size_t penumbra_count = pins.size() * 2;
1153 penumbra_count += trigs.
size() * 4;
1154 vertex_count += penumbra_count;
1155 triangle_count += penumbra_count;
1157 vertices_.reserve(vertex_count);
1158 gaussians_.reserve(vertex_count);
1159 indices_.reserve(triangle_count * 3);
1172 PopulateUmbraVertices(pins, list, centroid);
1197 const UmbraPin* p_prev_pin = &pins.back();
1205 Point last_penumbra_point =
1206 p_prev_pin->path_vertex + p_prev_pin->penumbra_delta;
1207 uint16_t last_penumbra_index = AppendVertex(last_penumbra_point, 0.0f);
1209 for (
const UmbraPin& pin : pins) {
1210 const UmbraPin* p_curr_pin = &pin;
1218 if (p_prev_pin->umbra_index != p_curr_pin->umbra_index) {
1236 AddTriangle(last_penumbra_index,
1237 p_prev_pin->umbra_index, p_curr_pin->umbra_index);
1242 Point new_penumbra_point =
1243 p_curr_pin->path_vertex + p_prev_pin->penumbra_delta;
1244 uint16_t new_penumbra_index = AppendVertex(new_penumbra_point, 0.0f);
1246 if (last_penumbra_index != new_penumbra_index) {
1247 AddTriangle(p_curr_pin->umbra_index, last_penumbra_index,
1248 new_penumbra_index);
1251 last_penumbra_point = new_penumbra_point;
1252 last_penumbra_index = new_penumbra_index;
1257 new_penumbra_point = p_curr_pin->path_vertex + p_curr_pin->penumbra_delta;
1258 new_penumbra_index =
1259 AppendFan(p_curr_pin, last_penumbra_point, new_penumbra_point,
1260 last_penumbra_index, trigs, direction);
1262 last_penumbra_point = new_penumbra_point;
1263 last_penumbra_index = new_penumbra_index;
1264 p_prev_pin = p_curr_pin;
1271 void PolygonInfo::PopulateUmbraVertices(std::vector<UmbraPin>& pins,
1272 UmbraPinLinkedList& list,
1273 const Point centroid) {
1277 FML_DCHECK(list.p_head_pin !=
nullptr);
1278 FML_DCHECK(vertices_.empty());
1279 FML_DCHECK(gaussians_.empty());
1280 FML_DCHECK(indices_.empty());
1283 uint16_t last_umbra_index = AppendVertex(centroid, umbra_gaussian_);
1284 FML_DCHECK(last_umbra_index == 0u);
1291 UmbraPin* p_next_umbra_pin = list.p_head_pin;
1292 UmbraPin* p_curr_umbra_pin = p_next_umbra_pin->p_prev;
1293 for (UmbraPin& pin : pins) {
1294 if (p_next_umbra_pin == &pin ||
1295 (pin.path_vertex.GetDistanceSquared(p_curr_umbra_pin->umbra_vertex) >
1296 pin.path_vertex.GetDistanceSquared(p_next_umbra_pin->umbra_vertex))) {
1300 p_curr_umbra_pin = p_next_umbra_pin;
1301 p_next_umbra_pin = p_next_umbra_pin->p_next;
1304 uint16_t new_umbra_index =
1305 AppendVertex(p_curr_umbra_pin->umbra_vertex, umbra_gaussian_);
1306 p_curr_umbra_pin->umbra_index = new_umbra_index;
1307 if (last_umbra_index != 0u) {
1308 AddTriangle(0u, last_umbra_index, new_umbra_index);
1310 last_umbra_index = new_umbra_index;
1312 if (p_curr_umbra_pin != &pin) {
1313 pin.umbra_vertex = p_curr_umbra_pin->umbra_vertex;
1314 pin.umbra_index = last_umbra_index;
1316 FML_DCHECK(pin.umbra_index != 0u);
1318 if (last_umbra_index != pins.front().umbra_index) {
1319 AddTriangle(0u, last_umbra_index, pins.front().umbra_index);
1326 uint16_t PolygonInfo::AppendFan(
const UmbraPin* p_curr_pin,
1329 uint16_t start_index,
1332 Point center = p_curr_pin->path_vertex;
1333 uint16_t center_index = p_curr_pin->umbra_index;
1334 uint16_t prev_index = start_index;
1338 size_t trig_count = trigs.
size();
1339 for (
size_t i = 1u; i < trig_count; i++) {
1340 Trig trig = trigs[i];
1341 Point fan_delta = (direction >= 0 ? trig : -trig) * start_delta;
1342 if (fan_delta.Cross(end_delta) * direction <= 0) {
1345 uint16_t cur_index = AppendVertex(center + fan_delta, 0.0f);
1346 if (prev_index != cur_index) {
1347 AddTriangle(center_index, prev_index, cur_index);
1348 prev_index = cur_index;
1350 if (i == trig_count - 1) {
1359 start_delta = fan_delta;
1362 uint16_t cur_index = AppendVertex(center + end_delta, 0.0f);
1363 if (prev_index != cur_index) {
1364 AddTriangle(center_index, prev_index, cur_index);
1371 uint16_t PolygonInfo::AppendVertex(
const Point& vertex,
Scalar gaussian) {
1372 FML_DCHECK(gaussian >= 0.0f && gaussian <= 1.0f);
1373 uint16_t index = vertices_.size();
1374 FML_DCHECK(index == gaussians_.size());
1376 FML_DCHECK(index <= std::numeric_limits<uint16_t>::max());
1378 FML_DCHECK(!gaussians_.empty() && !vertices_.empty());
1379 if (gaussian == gaussians_.back() && vertex == vertices_.back()) {
1383 vertices_.push_back(vertex);
1384 gaussians_.push_back(gaussian);
1389 void PolygonInfo::AddTriangle(uint16_t v0, uint16_t v1, uint16_t v2) {
1390 FML_DCHECK(std::max(std::max(v0, v1), v2) < vertices_.size());
1391 indices_.push_back(v0);
1392 indices_.push_back(v1);
1393 indices_.push_back(v2);
1401 std::make_shared<ShadowVertices>();
1411 : shadow_vertices_(MakeAmbientShadowVertices(tessellator,
1417 return shadow_vertices_ !=
nullptr;
1421 return shadow_vertices_ !=
nullptr && shadow_vertices_->IsEmpty();
1426 return shadow_vertices_;
1430 return std::move(shadow_vertices_);
1436 using VS = ShadowVerticesVertexShader;
1441 vertex_count *
sizeof(VS::PerVertexData),
alignof(VS::PerVertexData),
1442 [&](uint8_t* data) {
1443 VS::PerVertexData* vtx_contents =
1444 reinterpret_cast<VS::PerVertexData*
>(data);
1445 for (
size_t i = 0u; i < vertex_count; i++) {
1447 .position = vertices_[i],
1448 .gaussian = gaussians_[i],
1454 const uint16_t* indices_data =
GetIndices().data();
1457 indices_data, index_count *
sizeof(uint16_t),
alignof(uint16_t));
1463 .vertex_buffer = vertex_buffer,
1464 .index_buffer = index_buffer,
1465 .vertex_count = index_count,
1477 Scalar trig_radius = PolygonInfo::GetTrigRadiusForHeight(occluder_height);
1480 PolygonInfo polygon(occluder_height);
1482 return polygon.CalculateConvexShadowMesh(source, matrix, trigs);
HostBuffer & GetTransientsIndexesBuffer() const
Retrieve the current host buffer for transient storage of indexes used for indexed draws.
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
Matrix GetShaderTransform(const RenderPass &pass) const
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
static std::pair< size_t, size_t > CountFillStorage(const PathSource &source, Scalar scale)
Render passes encode render commands directed as one specific render target into an underlying comman...
const std::shared_ptr< ShadowVertices > & GetShadowVertices() const
ShadowPathGeometry(Tessellator &tessellator, const Matrix &matrix, const PathSource &source, Scalar occluder_height)
bool IsEmpty() const
Returns true if this shadow has no effect, is not visible.
const std::shared_ptr< ShadowVertices > TakeShadowVertices()
static std::shared_ptr< ShadowVertices > MakeAmbientShadowVertices(Tessellator &tessellator, const PathSource &source, Scalar occluder_height, const Matrix &matrix)
size_t GetVertexCount() const
const std::vector< uint16_t > & GetIndices() const
std::optional< Rect > GetBounds() const
static const std::shared_ptr< ShadowVertices > kEmpty
GeometryResult GetPositionBuffer(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const
size_t GetIndexCount() const
The count of the indices that define the mesh.
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Trigs GetTrigsForDeviceRadius(Scalar pixel_radius)
constexpr float kEhCloseEnough
constexpr bool ScalarNearlyZero(Scalar x, Scalar tolerance=kEhCloseEnough)
LinePipeline::VertexShader VS
A 4x4 matrix using column-major storage.
static constexpr TPoint Round(const TPoint< U > &other)
constexpr TPoint Normalize() const
constexpr TPoint PerpendicularRight() const
constexpr static std::optional< TRect > MakePointBounds(const U &value)
A structure to store the sine and cosine of an angle.