summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp28
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h8
2 files changed, 32 insertions, 4 deletions
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index d70d62ede..6ed67efeb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -158,12 +158,34 @@ void RasterizerOpenGL::Reset() {
158 res_cache.InvalidateAll(); 158 res_cache.InvalidateAll();
159} 159}
160 160
161/**
162 * This is a helper function to resolve an issue with opposite quaternions being interpolated by
163 * OpenGL. See below for a detailed description of this issue (yuriks):
164 *
165 * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you
166 * interpolate two quaternions that are opposite, instead of going from one rotation to another
167 * using the shortest path, you'll go around the longest path. You can test if two quaternions are
168 * opposite by checking if Dot(Q1, W2) < 0. In that case, you can flip either of them, therefore
169 * making Dot(-Q1, W2) positive.
170 *
171 * NOTE: This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This
172 * should be correct for nearly all cases, however a more correct implementation (but less trivial
173 * and perhaps unnecessary) would be to handle this per-fragment, by interpolating the quaternions
174 * manually using two Lerps, and doing this correction before each Lerp.
175 */
176static bool AreQuaternionsOpposite(Math::Vec4<Pica::float24> qa, Math::Vec4<Pica::float24> qb) {
177 Math::Vec4f a{ qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32() };
178 Math::Vec4f b{ qb.x.ToFloat32(), qb.y.ToFloat32(), qb.z.ToFloat32(), qb.w.ToFloat32() };
179
180 return (Math::Dot(a, b) < 0.f);
181}
182
161void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0, 183void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0,
162 const Pica::Shader::OutputVertex& v1, 184 const Pica::Shader::OutputVertex& v1,
163 const Pica::Shader::OutputVertex& v2) { 185 const Pica::Shader::OutputVertex& v2) {
164 vertex_batch.emplace_back(v0); 186 vertex_batch.emplace_back(v0, false);
165 vertex_batch.emplace_back(v1); 187 vertex_batch.emplace_back(v1, AreQuaternionsOpposite(v0.quat, v1.quat));
166 vertex_batch.emplace_back(v2); 188 vertex_batch.emplace_back(v2, AreQuaternionsOpposite(v0.quat, v2.quat));
167} 189}
168 190
169void RasterizerOpenGL::DrawTriangles() { 191void RasterizerOpenGL::DrawTriangles() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b9c1d61bd..99266854c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -248,7 +248,7 @@ private:
248 248
249 /// Structure that the hardware rendered vertices are composed of 249 /// Structure that the hardware rendered vertices are composed of
250 struct HardwareVertex { 250 struct HardwareVertex {
251 HardwareVertex(const Pica::Shader::OutputVertex& v) { 251 HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) {
252 position[0] = v.pos.x.ToFloat32(); 252 position[0] = v.pos.x.ToFloat32();
253 position[1] = v.pos.y.ToFloat32(); 253 position[1] = v.pos.y.ToFloat32();
254 position[2] = v.pos.z.ToFloat32(); 254 position[2] = v.pos.z.ToFloat32();
@@ -270,6 +270,12 @@ private:
270 view[0] = v.view.x.ToFloat32(); 270 view[0] = v.view.x.ToFloat32();
271 view[1] = v.view.y.ToFloat32(); 271 view[1] = v.view.y.ToFloat32();
272 view[2] = v.view.z.ToFloat32(); 272 view[2] = v.view.z.ToFloat32();
273
274 if (flip_quaternion) {
275 for (float& x : normquat) {
276 x = -x;
277 }
278 }
273 } 279 }
274 280
275 GLfloat position[4]; 281 GLfloat position[4];