clipper.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #include <vector>
  5. #include "clipper.h"
  6. #include "pica.h"
  7. #include "rasterizer.h"
  8. #include "vertex_shader.h"
  9. namespace Pica {
  10. namespace Clipper {
  11. struct ClippingEdge {
  12. public:
  13. enum Type {
  14. POS_X = 0,
  15. NEG_X = 1,
  16. POS_Y = 2,
  17. NEG_Y = 3,
  18. POS_Z = 4,
  19. NEG_Z = 5,
  20. };
  21. ClippingEdge(Type type, float24 position) : type(type), pos(position) {}
  22. bool IsInside(const OutputVertex& vertex) const {
  23. switch (type) {
  24. case POS_X: return vertex.pos.x <= pos * vertex.pos.w;
  25. case NEG_X: return vertex.pos.x >= pos * vertex.pos.w;
  26. case POS_Y: return vertex.pos.y <= pos * vertex.pos.w;
  27. case NEG_Y: return vertex.pos.y >= pos * vertex.pos.w;
  28. // TODO: Check z compares ... should be 0..1 instead?
  29. case POS_Z: return vertex.pos.z <= pos * vertex.pos.w;
  30. default:
  31. case NEG_Z: return vertex.pos.z >= pos * vertex.pos.w;
  32. }
  33. }
  34. bool IsOutSide(const OutputVertex& vertex) const {
  35. return !IsInside(vertex);
  36. }
  37. OutputVertex GetIntersection(const OutputVertex& v0, const OutputVertex& v1) const {
  38. auto dotpr = [this](const OutputVertex& vtx) {
  39. switch (type) {
  40. case POS_X: return vtx.pos.x - vtx.pos.w;
  41. case NEG_X: return -vtx.pos.x - vtx.pos.w;
  42. case POS_Y: return vtx.pos.y - vtx.pos.w;
  43. case NEG_Y: return -vtx.pos.y - vtx.pos.w;
  44. // TODO: Verify z clipping
  45. case POS_Z: return vtx.pos.z - vtx.pos.w;
  46. default:
  47. case NEG_Z: return -vtx.pos.w;
  48. }
  49. };
  50. float24 dp = dotpr(v0);
  51. float24 dp_prev = dotpr(v1);
  52. float24 factor = dp_prev / (dp_prev - dp);
  53. return OutputVertex::Lerp(factor, v0, v1);
  54. }
  55. private:
  56. Type type;
  57. float24 pos;
  58. };
  59. static void InitScreenCoordinates(OutputVertex& vtx)
  60. {
  61. struct {
  62. float24 halfsize_x;
  63. float24 offset_x;
  64. float24 halfsize_y;
  65. float24 offset_y;
  66. float24 zscale;
  67. float24 offset_z;
  68. } viewport;
  69. viewport.halfsize_x = float24::FromRawFloat24(registers.viewport_size_x);
  70. viewport.halfsize_y = float24::FromRawFloat24(registers.viewport_size_y);
  71. viewport.offset_x = float24::FromFloat32(static_cast<float>(registers.viewport_corner.x));
  72. viewport.offset_y = float24::FromFloat32(static_cast<float>(registers.viewport_corner.y));
  73. viewport.zscale = float24::FromRawFloat24(registers.viewport_depth_range);
  74. viewport.offset_z = float24::FromRawFloat24(registers.viewport_depth_far_plane);
  75. // TODO: Not sure why the viewport width needs to be divided by 2 but the viewport height does not
  76. vtx.screenpos[0] = (vtx.pos.x / vtx.pos.w + float24::FromFloat32(1.0)) * viewport.halfsize_x + viewport.offset_x;
  77. vtx.screenpos[1] = (vtx.pos.y / vtx.pos.w + float24::FromFloat32(1.0)) * viewport.halfsize_y + viewport.offset_y;
  78. vtx.screenpos[2] = viewport.offset_z - vtx.pos.z / vtx.pos.w * viewport.zscale;
  79. }
  80. void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) {
  81. // TODO (neobrain):
  82. // The list of output vertices has some fixed maximum size,
  83. // however I haven't taken the time to figure out what it is exactly.
  84. // For now, we hence just assume a maximal size of 1000 vertices.
  85. const size_t max_vertices = 1000;
  86. std::vector<OutputVertex> buffer_vertices;
  87. std::vector<OutputVertex*> output_list{ &v0, &v1, &v2 };
  88. // Make sure to reserve space for all vertices.
  89. // Without this, buffer reallocation would invalidate references.
  90. buffer_vertices.reserve(max_vertices);
  91. // Simple implementation of the Sutherland-Hodgman clipping algorithm.
  92. // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here)
  93. for (auto edge : { ClippingEdge(ClippingEdge::POS_X, float24::FromFloat32(+1.0)),
  94. ClippingEdge(ClippingEdge::NEG_X, float24::FromFloat32(-1.0)),
  95. ClippingEdge(ClippingEdge::POS_Y, float24::FromFloat32(+1.0)),
  96. ClippingEdge(ClippingEdge::NEG_Y, float24::FromFloat32(-1.0)),
  97. ClippingEdge(ClippingEdge::POS_Z, float24::FromFloat32(+1.0)),
  98. ClippingEdge(ClippingEdge::NEG_Z, float24::FromFloat32(-1.0)) }) {
  99. const std::vector<OutputVertex*> input_list = output_list;
  100. output_list.clear();
  101. const OutputVertex* reference_vertex = input_list.back();
  102. for (const auto& vertex : input_list) {
  103. // NOTE: This algorithm changes vertex order in some cases!
  104. if (edge.IsInside(*vertex)) {
  105. if (edge.IsOutSide(*reference_vertex)) {
  106. buffer_vertices.push_back(edge.GetIntersection(*vertex, *reference_vertex));
  107. output_list.push_back(&(buffer_vertices.back()));
  108. }
  109. output_list.push_back(vertex);
  110. } else if (edge.IsInside(*reference_vertex)) {
  111. buffer_vertices.push_back(edge.GetIntersection(*vertex, *reference_vertex));
  112. output_list.push_back(&(buffer_vertices.back()));
  113. }
  114. reference_vertex = vertex;
  115. }
  116. // Need to have at least a full triangle to continue...
  117. if (output_list.size() < 3)
  118. return;
  119. }
  120. InitScreenCoordinates(*(output_list[0]));
  121. InitScreenCoordinates(*(output_list[1]));
  122. for (size_t i = 0; i < output_list.size() - 2; i ++) {
  123. OutputVertex& vtx0 = *(output_list[0]);
  124. OutputVertex& vtx1 = *(output_list[i+1]);
  125. OutputVertex& vtx2 = *(output_list[i+2]);
  126. InitScreenCoordinates(vtx2);
  127. DEBUG_LOG(GPU,
  128. "Triangle %lu/%lu (%lu buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), "
  129. "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and "
  130. "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)",
  131. i,output_list.size(), buffer_vertices.size(),
  132. vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),
  133. vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(),
  134. vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(),
  135. vtx0.screenpos.x.ToFloat32(), vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(),
  136. vtx1.screenpos.x.ToFloat32(), vtx1.screenpos.y.ToFloat32(), vtx1.screenpos.z.ToFloat32(),
  137. vtx2.screenpos.x.ToFloat32(), vtx2.screenpos.y.ToFloat32(), vtx2.screenpos.z.ToFloat32());
  138. Rasterizer::ProcessTriangle(vtx0, vtx1, vtx2);
  139. }
  140. }
  141. } // namespace
  142. } // namespace