summaryrefslogtreecommitdiff
path: root/src/video_core/clipper.cpp
diff options
context:
space:
mode:
authorGravatar Tony Wasserka2014-08-12 13:55:41 +0200
committerGravatar Tony Wasserka2014-08-12 13:55:41 +0200
commit36cabe35cc15a6590f5d18be695ae505a946cb06 (patch)
tree241e6d8b36e6ab9921ef7afb71e7350e52862e2a /src/video_core/clipper.cpp
parentMerge pull request #38 from neobrain/replace_registerset (diff)
parentPica: Add basic rasterizer. (diff)
downloadyuzu-36cabe35cc15a6590f5d18be695ae505a946cb06.tar.gz
yuzu-36cabe35cc15a6590f5d18be695ae505a946cb06.tar.xz
yuzu-36cabe35cc15a6590f5d18be695ae505a946cb06.zip
Merge pull request #37 from neobrain/pica
Initial work on Pica rendering.
Diffstat (limited to 'src/video_core/clipper.cpp')
-rw-r--r--src/video_core/clipper.cpp179
1 files changed, 179 insertions, 0 deletions
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp
new file mode 100644
index 000000000..b7180328c
--- /dev/null
+++ b/src/video_core/clipper.cpp
@@ -0,0 +1,179 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <vector>
6
7#include "clipper.h"
8#include "pica.h"
9#include "rasterizer.h"
10#include "vertex_shader.h"
11
12namespace Pica {
13
14namespace Clipper {
15
16struct ClippingEdge {
17public:
18 enum Type {
19 POS_X = 0,
20 NEG_X = 1,
21 POS_Y = 2,
22 NEG_Y = 3,
23 POS_Z = 4,
24 NEG_Z = 5,
25 };
26
27 ClippingEdge(Type type, float24 position) : type(type), pos(position) {}
28
29 bool IsInside(const OutputVertex& vertex) const {
30 switch (type) {
31 case POS_X: return vertex.pos.x <= pos * vertex.pos.w;
32 case NEG_X: return vertex.pos.x >= pos * vertex.pos.w;
33 case POS_Y: return vertex.pos.y <= pos * vertex.pos.w;
34 case NEG_Y: return vertex.pos.y >= pos * vertex.pos.w;
35
36 // TODO: Check z compares ... should be 0..1 instead?
37 case POS_Z: return vertex.pos.z <= pos * vertex.pos.w;
38
39 default:
40 case NEG_Z: return vertex.pos.z >= pos * vertex.pos.w;
41 }
42 }
43
44 bool IsOutSide(const OutputVertex& vertex) const {
45 return !IsInside(vertex);
46 }
47
48 OutputVertex GetIntersection(const OutputVertex& v0, const OutputVertex& v1) const {
49 auto dotpr = [this](const OutputVertex& vtx) {
50 switch (type) {
51 case POS_X: return vtx.pos.x - vtx.pos.w;
52 case NEG_X: return -vtx.pos.x - vtx.pos.w;
53 case POS_Y: return vtx.pos.y - vtx.pos.w;
54 case NEG_Y: return -vtx.pos.y - vtx.pos.w;
55
56 // TODO: Verify z clipping
57 case POS_Z: return vtx.pos.z - vtx.pos.w;
58
59 default:
60 case NEG_Z: return -vtx.pos.w;
61 }
62 };
63
64 float24 dp = dotpr(v0);
65 float24 dp_prev = dotpr(v1);
66 float24 factor = dp_prev / (dp_prev - dp);
67
68 return OutputVertex::Lerp(factor, v0, v1);
69 }
70
71private:
72 Type type;
73 float24 pos;
74};
75
76static void InitScreenCoordinates(OutputVertex& vtx)
77{
78 struct {
79 float24 halfsize_x;
80 float24 offset_x;
81 float24 halfsize_y;
82 float24 offset_y;
83 float24 zscale;
84 float24 offset_z;
85 } viewport;
86
87 viewport.halfsize_x = float24::FromRawFloat24(registers.viewport_size_x);
88 viewport.halfsize_y = float24::FromRawFloat24(registers.viewport_size_y);
89 viewport.offset_x = float24::FromFloat32(registers.viewport_corner.x);
90 viewport.offset_y = float24::FromFloat32(registers.viewport_corner.y);
91 viewport.zscale = float24::FromRawFloat24(registers.viewport_depth_range);
92 viewport.offset_z = float24::FromRawFloat24(registers.viewport_depth_far_plane);
93
94 // TODO: Not sure why the viewport width needs to be divided by 2 but the viewport height does not
95 vtx.screenpos[0] = (vtx.pos.x / vtx.pos.w + float24::FromFloat32(1.0)) * viewport.halfsize_x / float24::FromFloat32(2.0) + viewport.offset_x;
96 vtx.screenpos[1] = (vtx.pos.y / vtx.pos.w + float24::FromFloat32(1.0)) * viewport.halfsize_y + viewport.offset_y;
97 vtx.screenpos[2] = viewport.offset_z - vtx.pos.z / vtx.pos.w * viewport.zscale;
98}
99
100void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) {
101
102 // TODO (neobrain):
103 // The list of output vertices has some fixed maximum size,
104 // however I haven't taken the time to figure out what it is exactly.
105 // For now, we hence just assume a maximal size of 1000 vertices.
106 const size_t max_vertices = 1000;
107 std::vector<OutputVertex> buffer_vertices;
108 std::vector<OutputVertex*> output_list{ &v0, &v1, &v2 };
109
110 // Make sure to reserve space for all vertices.
111 // Without this, buffer reallocation would invalidate references.
112 buffer_vertices.reserve(max_vertices);
113
114 // Simple implementation of the Sutherland-Hodgman clipping algorithm.
115 // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here)
116 for (auto edge : { ClippingEdge(ClippingEdge::POS_X, float24::FromFloat32(+1.0)),
117 ClippingEdge(ClippingEdge::NEG_X, float24::FromFloat32(-1.0)),
118 ClippingEdge(ClippingEdge::POS_Y, float24::FromFloat32(+1.0)),
119 ClippingEdge(ClippingEdge::NEG_Y, float24::FromFloat32(-1.0)),
120 ClippingEdge(ClippingEdge::POS_Z, float24::FromFloat32(+1.0)),
121 ClippingEdge(ClippingEdge::NEG_Z, float24::FromFloat32(-1.0)) }) {
122
123 const std::vector<OutputVertex*> input_list = output_list;
124 output_list.clear();
125
126 const OutputVertex* reference_vertex = input_list.back();
127
128 for (const auto& vertex : input_list) {
129 // NOTE: This algorithm changes vertex order in some cases!
130 if (edge.IsInside(*vertex)) {
131 if (edge.IsOutSide(*reference_vertex)) {
132 buffer_vertices.push_back(edge.GetIntersection(*vertex, *reference_vertex));
133 output_list.push_back(&(buffer_vertices.back()));
134 }
135
136 output_list.push_back(vertex);
137 } else if (edge.IsInside(*reference_vertex)) {
138 buffer_vertices.push_back(edge.GetIntersection(*vertex, *reference_vertex));
139 output_list.push_back(&(buffer_vertices.back()));
140 }
141
142 reference_vertex = vertex;
143 }
144
145 // Need to have at least a full triangle to continue...
146 if (output_list.size() < 3)
147 return;
148 }
149
150 InitScreenCoordinates(*(output_list[0]));
151 InitScreenCoordinates(*(output_list[1]));
152
153 for (int i = 0; i < output_list.size() - 2; i ++) {
154 OutputVertex& vtx0 = *(output_list[0]);
155 OutputVertex& vtx1 = *(output_list[i+1]);
156 OutputVertex& vtx2 = *(output_list[i+2]);
157
158 InitScreenCoordinates(vtx2);
159
160 DEBUG_LOG(GPU,
161 "Triangle %d/%d (%d buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), "
162 "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and "
163 "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)",
164 i,output_list.size(), buffer_vertices.size(),
165 vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),output_list.size(),
166 vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(),
167 vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(),
168 vtx0.screenpos.x.ToFloat32(), vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(),
169 vtx1.screenpos.x.ToFloat32(), vtx1.screenpos.y.ToFloat32(), vtx1.screenpos.z.ToFloat32(),
170 vtx2.screenpos.x.ToFloat32(), vtx2.screenpos.y.ToFloat32(), vtx2.screenpos.z.ToFloat32());
171
172 Rasterizer::ProcessTriangle(vtx0, vtx1, vtx2);
173 }
174}
175
176
177} // namespace
178
179} // namespace