Browse Source

Merge branch '12.0-development' into metal

Alex Szpakowski 3 years ago
parent
commit
f93d167a47

+ 43 - 0
src/common/Matrix.cpp

@@ -181,6 +181,32 @@ const float *Matrix4::getElements() const
 	return e;
 }
 
+void Matrix4::setRow(int r, const Vector4 &v)
+{
+	e[0 * 4 + r] = v.x;
+	e[1 * 4 + r] = v.y;
+	e[2 * 4 + r] = v.z;
+	e[3 * 4 + r] = v.w;
+}
+
+Vector4 Matrix4::getRow(int r) const
+{
+	return Vector4(e[0 * 4 + r], e[1 * 4 + r], e[2 * 4 + r], e[3 * 4 + r]);
+}
+
+void Matrix4::setColumn(int c, const Vector4 &v)
+{
+	e[c * 4 + 0] = v.x;
+	e[c * 4 + 1] = v.y;
+	e[c * 4 + 2] = v.z;
+	e[c * 4 + 3] = v.w;
+}
+
+Vector4 Matrix4::getColumn(int c) const
+{
+	return Vector4(e[c * 4 + 0], e[c * 4 + 1], e[c * 4 + 2], e[c * 4 + 3]);
+}
+
 void Matrix4::setIdentity()
 {
 	memset(e, 0, sizeof(float)*16);
@@ -430,6 +456,23 @@ Matrix4 Matrix4::ortho(float left, float right, float bottom, float top, float n
 	return m;
 }
 
+Matrix4 Matrix4::perspective(float verticalfov, float aspect, float near, float far)
+{
+	Matrix4 m;
+
+	float cotangent = 1.0f / tanf(verticalfov * 0.5f);
+
+	m.e[0] = cotangent / aspect;
+	m.e[5] = cotangent;
+	m.e[10] = (far + near) / (near - far);
+	m.e[11] = -1.0f;
+
+	m.e[14] = 2.0f * near * far / (near - far);
+	m.e[15] = 0.0f;
+
+	return m;
+}
+
 /**
  * | e0 e3 e6 |
  * | e1 e4 e7 |

+ 11 - 0
src/common/Matrix.h

@@ -23,6 +23,7 @@
 
 // LOVE
 #include "math.h"
+#include "Vector.h"
 
 namespace love
 {
@@ -88,6 +89,11 @@ public:
 	 **/
 	const float *getElements() const;
 
+	void setRow(int r, const Vector4 &v);
+	Vector4 getRow(int r) const;
+
+	void setColumn(int c, const Vector4 &v);
+	Vector4 getColumn(int c) const;
 	/**
 	 * Resets this Matrix to the identity matrix.
 	 **/
@@ -222,6 +228,11 @@ public:
 	 **/
 	static Matrix4 ortho(float left, float right, float bottom, float top, float near, float far);
 
+	/**
+	 * Creates a new perspective projection matrix.
+	 **/
+	static Matrix4 perspective(float verticalfov, float aspect, float near, float far);
+
 private:
 
 	/**

+ 92 - 3
src/modules/graphics/Graphics.cpp

@@ -179,7 +179,7 @@ Graphics::Graphics()
 	, created(false)
 	, active(true)
 	, batchedDrawState()
-	, projectionMatrix()
+	, deviceProjectionMatrix()
 	, renderTargetSwitchCount(0)
 	, drawCalls(0)
 	, drawCallsBatched(0)
@@ -541,6 +541,11 @@ void Graphics::restoreState(const DisplayState &s)
 	setWireframe(s.wireframe);
 
 	setDefaultSamplerState(s.defaultSamplerState);
+
+	if (s.useCustomProjection)
+		updateDeviceProjection(s.customProjection);
+	else
+		resetProjection();
 }
 
 void Graphics::restoreStateChecked(const DisplayState &s)
@@ -625,6 +630,11 @@ void Graphics::restoreStateChecked(const DisplayState &s)
 		setWireframe(s.wireframe);
 
 	setDefaultSamplerState(s.defaultSamplerState);
+
+	if (s.useCustomProjection)
+		setCustomProjection(s.customProjection);
+	else if (cur.useCustomProjection)
+		resetProjection();
 }
 
 Colorf Graphics::getColor() const
@@ -878,6 +888,8 @@ void Graphics::setRenderTargets(const RenderTargets &rts)
 	std::swap(state.renderTargets, refs);
 
 	renderTargetSwitchCount++;
+
+	resetProjection();
 }
 
 void Graphics::setRenderTarget()
@@ -892,6 +904,8 @@ void Graphics::setRenderTarget()
 
 	state.renderTargets = RenderTargetsStrongRef();
 	renderTargetSwitchCount++;
+
+	resetProjection();
 }
 
 Graphics::RenderTargets Graphics::getRenderTargets() const
@@ -2097,9 +2111,9 @@ const Matrix4 &Graphics::getTransform() const
 	return transformStack.back();
 }
 
-const Matrix4 &Graphics::getProjection() const
+const Matrix4 &Graphics::getDeviceProjection() const
 {
-	return projectionMatrix;
+	return deviceProjectionMatrix;
 }
 
 void Graphics::pushTransform()
@@ -2179,6 +2193,81 @@ Vector2 Graphics::inverseTransformPoint(Vector2 point)
 	return p;
 }
 
+void Graphics::setOrthoProjection(float w, float h, float near, float far)
+{
+	if (near >= far)
+		throw love::Exception("Orthographic projection Z far value must be greater than the Z near value.");
+
+	Matrix4 m = Matrix4::ortho(0.0f, w, 0.0f, h, near, far);
+	setCustomProjection(m);
+}
+
+void Graphics::setPerspectiveProjection(float verticalfov, float aspect, float near, float far)
+{
+	if (near <= 0.0f)
+		throw love::Exception("Perspective projection Z near value must be greater than 0.");
+
+	if (near >= far)
+		throw love::Exception("Perspective projection Z far value must be greater than the Z near value.");
+
+	Matrix4 m = Matrix4::perspective(verticalfov, aspect, near, far);
+	setCustomProjection(m);
+}
+
+void Graphics::setCustomProjection(const Matrix4 &m)
+{
+	flushBatchedDraws();
+
+	auto &state = states.back();
+
+	state.useCustomProjection = true;
+	state.customProjection = m;
+
+	updateDeviceProjection(m);
+}
+
+void Graphics::resetProjection()
+{
+	flushBatchedDraws();
+
+	auto &state = states.back();
+	int w = getWidth();
+	int h = getHeight();
+
+	const auto &rt = state.renderTargets.getFirstTarget();
+	if (rt.texture.get())
+	{
+		w = rt.texture->getWidth(rt.mipmap);
+		h = rt.texture->getHeight(rt.mipmap);
+	}
+
+	state.useCustomProjection = false;
+
+	updateDeviceProjection(Matrix4::ortho(0.0f, w, 0.0f, h, -10.0f, 10.0f));
+}
+
+void Graphics::updateDeviceProjection(const Matrix4 &projection)
+{
+	// Note: graphics implementations define computeDeviceProjection.
+	deviceProjectionMatrix = computeDeviceProjection(projection, isRenderTargetActive());
+}
+
+Matrix4 Graphics::calculateDeviceProjection(const Matrix4 &projection, uint32 flags) const
+{
+	Matrix4 m = projection;
+	bool reverseZ = (flags & DEVICE_PROJECTION_REVERSE_Z) != 0;
+
+	if (flags & DEVICE_PROJECTION_FLIP_Y)
+		m.setRow(1, -m.getRow(1));
+
+	if (flags & DEVICE_PROJECTION_Z_01) // Go from Z [-1, 1] to Z [0, 1].
+		m.setRow(2, m.getRow(2) * (reverseZ ? -0.5f : 0.5f) + m.getRow(3));
+	else if (reverseZ)
+		m.setRow(2, -m.getRow(2));
+
+	return m;
+}
+
 STRINGMAP_CLASS_BEGIN(Graphics, Graphics::DrawMode, Graphics::DRAW_MAX_ENUM, drawMode)
 {
 	{ "line", Graphics::DRAW_LINE },

+ 23 - 2
src/modules/graphics/Graphics.h

@@ -834,7 +834,7 @@ public:
 	void pop();
 
 	const Matrix4 &getTransform() const;
-	const Matrix4 &getProjection() const;
+	const Matrix4 &getDeviceProjection() const;
 
 	void rotate(float r);
 	void scale(float x, float y = 1.0f);
@@ -848,6 +848,13 @@ public:
 	Vector2 transformPoint(Vector2 point);
 	Vector2 inverseTransformPoint(Vector2 point);
 
+	void setOrthoProjection(float w, float h, float near, float far);
+	void setPerspectiveProjection(float verticalfov, float aspect, float near, float far);
+	void setCustomProjection(const Matrix4 &m);
+	void resetProjection();
+
+	virtual Matrix4 computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const = 0;
+
 	virtual void draw(const DrawCommand &cmd) = 0;
 	virtual void draw(const DrawIndexedCommand &cmd) = 0;
 	virtual void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, Texture *texture) = 0;
@@ -882,6 +889,14 @@ public:
 
 protected:
 
+	enum DeviceProjectionFlags
+	{
+		DEVICE_PROJECTION_DEFAULT = 0,
+		DEVICE_PROJECTION_FLIP_Y = (1 << 0),
+		DEVICE_PROJECTION_Z_01 = (1 << 1),
+		DEVICE_PROJECTION_REVERSE_Z = (1 << 2),
+	};
+
 	struct DisplayState
 	{
 		DisplayState();
@@ -917,6 +932,9 @@ protected:
 
 		bool wireframe = false;
 
+		bool useCustomProjection = false;
+		Matrix4 customProjection;
+
 		// Default mipmap filter is set in the DisplayState constructor.
 		SamplerState defaultSamplerState = SamplerState();
 	};
@@ -978,6 +996,9 @@ protected:
 	void pushIdentityTransform();
 	void popTransform();
 
+	void updateDeviceProjection(const Matrix4 &projection);
+	Matrix4 calculateDeviceProjection(const Matrix4 &projection, uint32 flags) const;
+
 	int width;
 	int height;
 	int pixelWidth;
@@ -993,7 +1014,7 @@ protected:
 	BatchedDrawState batchedDrawState;
 
 	std::vector<Matrix4> transformStack;
-	Matrix4 projectionMatrix;
+	Matrix4 deviceProjectionMatrix;
 
 	std::vector<double> pixelScaleStack;
 

+ 15 - 9
src/modules/graphics/opengl/Graphics.cpp

@@ -177,6 +177,18 @@ love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, co
 	return new Buffer(this, settings, format, data, size, arraylength);
 }
 
+Matrix4 Graphics::computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const
+{
+	uint32 flags = DEVICE_PROJECTION_DEFAULT;
+
+	// The projection matrix is flipped compared to rendering to a texture, due
+	// to OpenGL considering (0,0) bottom-left instead of top-left.
+	if (!rendertotexture)
+		flags |= DEVICE_PROJECTION_FLIP_Y;
+
+	return calculateDeviceProjection(projection, flags);
+}
+
 void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
 {
 	this->width = width;
@@ -194,8 +206,7 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
 		if (states.back().scissor)
 			setScissor(states.back().scissorRect);
 
-		// Set up the projection matrix
-		projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0, -10.0f, 10.0f);
+		resetProjection();
 	}
 
 	updateBackbuffer(width, height, pixelwidth, pixelheight, requestedBackbufferMSAA);
@@ -731,7 +742,7 @@ void Graphics::setDebug(bool enable)
 	::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
 }
 
-void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBtexture)
+void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int /*w*/, int /*h*/, int pixelw, int pixelh, bool hasSRGBtexture)
 {
 	const DisplayState &state = states.back();
 
@@ -745,19 +756,14 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h,
 	if (iswindow)
 	{
 		gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
-
-		// The projection matrix is flipped compared to rendering to a texture,
-		// due to OpenGL considering (0,0) bottom-left instead of top-left.
-		projectionMatrix = Matrix4::ortho(0.0, (float) w, (float) h, 0.0, -10.0f, 10.0f);
 	}
 	else
 	{
 		bindCachedFBO(rts);
 
-		projectionMatrix = Matrix4::ortho(0.0, (float) w, 0.0, (float) h, -10.0f, 10.0f);
-
 		// Flip front face winding when rendering to a texture, since our
 		// projection matrix is flipped.
+		// Note: projection matrix is set at a higher level.
 		vertexwinding = vertexwinding == WINDING_CW ? WINDING_CCW : WINDING_CW;
 	}
 

+ 2 - 0
src/modules/graphics/opengl/Graphics.h

@@ -62,6 +62,8 @@ public:
 	love::graphics::Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) override;
 	love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
 
+	Matrix4 computeDeviceProjection(const Matrix4 &projection, bool rendertotexture) const override;
+
 	void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
 	bool setMode(void *context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) override;
 	void unSetMode() override;

+ 1 - 1
src/modules/graphics/opengl/Shader.cpp

@@ -969,7 +969,7 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW,
 	BuiltinUniformData data;
 
 	data.transformMatrix = gfx->getTransform();
-	data.projectionMatrix = gfx->getProjection();
+	data.projectionMatrix = gfx->getDeviceProjection();
 
 	// The normal matrix is the transpose of the inverse of the rotation portion
 	// (top-left 3x3) of the transform matrix.

+ 40 - 7
src/modules/graphics/wrap_Graphics.cpp

@@ -1836,21 +1836,22 @@ int w_newIndexBuffer(lua_State *L)
 	return 1;
 }
 
-static PrimitiveType luax_optmeshdrawmode(lua_State *L, int idx, PrimitiveType def)
+static PrimitiveType luax_checkmeshdrawmode(lua_State *L, int idx)
 {
-	const char *modestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx);
+	const char *modestr = luaL_checkstring(L, idx);
 
-	if (modestr && !getConstant(modestr, def))
-		luax_enumerror(L, "mesh draw mode", getConstants(def), modestr);
+	PrimitiveType mode = PRIMITIVE_TRIANGLES;
+	if (!getConstant(modestr, mode))
+		luax_enumerror(L, "mesh draw mode", getConstants(mode), modestr);
 
-	return def;
+	return mode;
 }
 
 static Mesh *newStandardMesh(lua_State *L)
 {
 	Mesh *t = nullptr;
 
-	PrimitiveType drawmode = luax_optmeshdrawmode(L, 2, PRIMITIVE_TRIANGLE_FAN);
+	PrimitiveType drawmode = luax_checkmeshdrawmode(L, 2);
 	BufferDataUsage usage = luax_optdatausage(L, 3, BUFFERDATAUSAGE_DYNAMIC);
 
 	std::vector<Buffer::DataDeclaration> format = Mesh::getDefaultVertexFormat();
@@ -1912,7 +1913,7 @@ static Mesh *newCustomMesh(lua_State *L)
 	// the number of vertices.
 	std::vector<Buffer::DataDeclaration> vertexformat;
 
-	PrimitiveType drawmode = luax_optmeshdrawmode(L, 3, PRIMITIVE_TRIANGLE_FAN);
+	PrimitiveType drawmode = luax_checkmeshdrawmode(L, 3);
 	BufferDataUsage usage = luax_optdatausage(L, 4, BUFFERDATAUSAGE_DYNAMIC);
 
 	lua_rawgeti(L, 1, 1);
@@ -3572,6 +3573,34 @@ int w_inverseTransformPoint(lua_State *L)
 	return 2;
 }
 
+int w_setOrthoProjection(lua_State *L)
+{
+	float w = (float) luaL_checknumber(L, 1);
+	float h = (float) luaL_checknumber(L, 2);
+	float near = (float) luaL_optnumber(L, 3, -10.0);
+	float far = (float) luaL_optnumber(L, 4, 10.0);
+
+	luax_catchexcept(L, [&]() { instance()->setOrthoProjection(w, h, near, far); });
+	return 0;
+}
+
+int w_setPerspectiveProjection(lua_State *L)
+{
+	float verticalfov = (float) luaL_checknumber(L, 1);
+	float aspect = (float) luaL_checknumber(L, 2);
+	float near = (float) luaL_checknumber(L, 3);
+	float far = (float) luaL_checknumber(L, 4);
+
+	luax_catchexcept(L, [&]() { instance()->setPerspectiveProjection(verticalfov, aspect, near, far); });
+	return 0;
+}
+
+int w_resetProjection(lua_State */*L*/)
+{
+	instance()->resetProjection();
+	return 0;
+}
+
 
 // List of functions to wrap.
 static const luaL_Reg functions[] =
@@ -3710,6 +3739,10 @@ static const luaL_Reg functions[] =
 	{ "transformPoint", w_transformPoint },
 	{ "inverseTransformPoint", w_inverseTransformPoint },
 
+	{ "setOrthoProjection", w_setOrthoProjection },
+	{ "setPerspectiveProjection", w_setPerspectiveProjection },
+	{ "resetProjection", w_resetProjection },
+
 	// Deprecated
 	{ "newImage", w_newImage },
 	{ "newArrayImage", w_newArrayImage },