tove2d icon indicating copy to clipboard operation
tove2d copied to clipboard

Mobile Platforms

Open turbo opened this issue 4 years ago • 7 comments

The README doesn't talk much about availability on mobile platforms. Should this library work on iOS/iPadOS and Android?

turbo avatar Nov 14 '19 01:11 turbo

That's a good question, I should add some info the the README.

I haven't tried compiling this for mobile platforms yet. In theory, it should work on Android, i.e. I don't foresee any fundamental problem compiling this on Android. On iOS it's more complicated since the library would have to be statically linked to the love2d binary, which would need some larger rewrites. tove2d would have to go into the love2d project and be compiled as one.

poke1024 avatar Nov 22 '19 10:11 poke1024

I managed to get it working on iOS -- though some of the demos seem to render black backgrounds and stuff (will figure it out). I had to make some changes to the shaders so they work on mobile GLES versions. Basically all of the errors are about lack of automatic casting with int and float types across binary operators. I ended up directly editing the generated files (sorry!) for now while trying to get it to work, but will try to make it more upstream mergeable soon. I also replaced the config.report stuff with a direct printf since callback functions aren't supported by FFI on mobile. For now here's a dump of the diffs in the generated code:

--- /Users/nikki/Development/tove2d/src/cpp/shader/gen.cpp	2020-01-12 19:32:33.000000000 -0800
+++ ./cpp/shader/gen.cpp	2020-01-12 22:25:14.000000000 -0800
@@ -255,7 +255,7 @@
 	float y = mix(gradient_pos.y, length(gradient_pos), gradient_scale.z);
 	y = gradient_scale.x + gradient_scale.y * y;
 
-	texture_pos = vec2((i + 0.5) / NUM_PAINTS, y);
+	texture_pos = vec2((float(i) + 0.5) / float(NUM_PAINTS), y);
 	return vertex_pos;
 }
 )GLSL";
diff -ru /Users/nikki/Development/tove2d/src/cpp/warn.h ./cpp/warn.h
--- /Users/nikki/Development/tove2d/src/cpp/warn.h	2020-01-12 19:32:33.000000000 -0800
+++ ./cpp/warn.h	2020-01-12 21:24:08.000000000 -0800
@@ -12,6 +12,8 @@
 #ifndef __TOVE_WARN
 #define __TOVE_WARN 1
 
+#include <stdio.h>
+
 #include "common.h"
 #include "interface.h"
 
@@ -30,6 +32,9 @@
     }
 
     inline void report(const char *s, ToveReportLevel l) {
+        if (l >= config.level) {
+          printf("%s\n", s);
+        }
         if (l >= config.level && config.report) {
             config.report(s, l);
         }
diff -ru /Users/nikki/Development/tove2d/src/glsl/fill.frag.inc ./glsl/fill.frag.inc
--- /Users/nikki/Development/tove2d/src/glsl/fill.frag.inc	2020-01-12 19:34:58.000000000 -0800
+++ ./glsl/fill.frag.inc	2020-01-12 23:10:21.000000000 -0800
@@ -92,7 +92,7 @@
 }
 
 vec2 curveTangent(vec4 bx, vec4 by, float t) {
-	vec3 c_dt = vec3(3 * t * t, 2 * t, 1);
+	vec3 c_dt = vec3(3.0f * t * t, 2.0f * t, 1);
 	vec2 tangent = vec2(dot(bx.xyz, c_dt), dot(by.xyz, c_dt));
 	if (dot(tangent, tangent) < 1e-2) {
 		// ugly, but necessary case; e.g. see "hearts" shape from demos
@@ -109,7 +109,7 @@
 	vec2 curvePoint = eval(bx, by, t);
 	vec4 state = vec4(t, distanceSquared(curvePoint, pos), s, -s);
 
-	int max_iterations = int(clamp(int(MAX_LINE_ITERATIONS * line_quality),
+	int max_iterations = int(clamp(int(float(MAX_LINE_ITERATIONS) * line_quality),
 		1, MAX_LINE_ITERATIONS));
 
 	for (int i = 0; i < max_iterations && state.y > tolerance; i++) {
@@ -158,7 +158,7 @@
 
 #if FILL_RULE == 0
 float clockwise(vec4 by, float t, float t2) {
-	vec3 c_dt = vec3(3 * t2, 2 * t, 1);
+	vec3 c_dt = vec3(3.0f * t2, 2.0f * t, 1);
 	return sign(dot(by.xyz, c_dt));
 }
 
@@ -196,12 +196,12 @@
 
 #if LINE_STYLE > 0
 bool checkLine(float curveId, vec2 position, vec2 axis1) {
-	vec4 bounds = Texel(curves, vec2(2.0 / CURVE_DATA_SIZE, curveId));
+	vec4 bounds = Texel(curves, vec2(2.0 / float(CURVE_DATA_SIZE), curveId));
 	vec2 off = clamp(position, bounds.xy, bounds.zw) - position;
 	if (abs(dot(off, axis1)) < LINE_OFFSET) {
-		vec4 bx = Texel(curves, vec2(0.0 / CURVE_DATA_SIZE, curveId));
-		vec4 by = Texel(curves, vec2(1.0 / CURVE_DATA_SIZE, curveId));
-		vec4 roots = Texel(curves, vec2(3.0 / CURVE_DATA_SIZE, curveId));
+		vec4 bx = Texel(curves, vec2(0.0 / float(CURVE_DATA_SIZE), curveId));
+		vec4 by = Texel(curves, vec2(1.0 / float(CURVE_DATA_SIZE), curveId));
+		vec4 roots = Texel(curves, vec2(3.0 / float(CURVE_DATA_SIZE), curveId));
 		return onCurveLine(position, bx, by, roots, LINE_OFFSET * LINE_OFFSET);
 	} else {
 		return false;
@@ -233,8 +233,8 @@
 			float b = P.z;
 			float c = P.w;
 
-			float D = sqrt(b * b - 4 * a * c);
-			float n = 2 * a;
+			float D = sqrt(b * b - 4.0 * a * c);
+			float n = 2.0 * a;
 			int z = contribute(bx, by, (-b + D) / n, position);
 			if (D > 0.0) {
 				z += contribute(bx, by, (-b - D) / n, position);
@@ -249,7 +249,7 @@
 
 		float A2 = A * A;
 		float Q = (3.0 * B - A2) / 9.0;
-		float R = (9.0 * A * B - 27.0 * C - 2 * A2 * A) / 54.0;
+		float R = (9.0 * A * B - 27.0 * C - 2.0f * A2 * A) / 54.0;
 
 		float A_third = A / 3.0;
 		float Q3 = Q * Q * Q;
@@ -266,14 +266,14 @@
 
 			if (abs(ST.x - ST.y) < 1e-2) { // complex roots?
 				// real part of complex root:
-				z += contribute(bx, by, -A_third - st / 2, position) * 2;
+				z += contribute(bx, by, -A_third - st / 2.0f, position) * 2;
 			}
 
 			return z;
 		} else { // distinct real roots
 			vec2 tmp = sqrt(max(vec2(0), vec2(-Q3, -Q)));
-			vec3 phi = vec3(acos(clamp(R / tmp.x, -1, 1))) + vec3(0, 2 * M_PI, 4 * M_PI);
-			vec3 t = 2 * tmp.y * cos(phi / 3.0) - A_third;
+			vec3 phi = vec3(acos(clamp(R / tmp.x, -1.0f, 1.0f))) + vec3(0, 2.0f * M_PI, 4.0f * M_PI);
+			vec3 t = 2.0f * tmp.y * cos(phi / 3.0) - A_third;
 			return contribute3(bx, by, t, position);
 		}
 	}
@@ -354,7 +354,7 @@
 	// reduce remaining numerical inaccuracies.
 	vec2 p0 = vec2(x0, y0);
 	vec2 p1 = vec2(x1, y1);
-	vec2 center = (p0 + p1) / 2;
+	vec2 center = (p0 + p1) / 2.0f;
 	vec2 margin = vec2(0.5);
 	vec2 npos = mix(min(p0 + margin, center), position, step(margin, d0));
 	npos = mix(max(center, p1 - margin), npos, step(margin, d1));
@@ -375,8 +375,8 @@
 #endif
 
 	vec2 blockPos = rayOnX ?
-		vec2(0, (at.y - 1 + 0.5) / float(LISTS_H) + 0.5f) :
-		vec2(0, (at.x - 1 + 0.5) / float(LISTS_H));
+		vec2(0, (float(at.y - 1) + 0.5) / float(LISTS_H) + 0.5f) :
+		vec2(0, (float(at.x - 1) + 0.5) / float(LISTS_H));
 #else // LUT_BANDS
 	vec2 position = raw_vertex_pos.xy;
 	bool rayOnX = textureUV.x > 0;
@@ -389,7 +389,7 @@
 	float rayPos = dot(position, axis1);
 	vec4 C = vec4(0.0, 0.0, 0.0, dot(position, axis2));
 
-	vec2 blockStep = vec2(1.0 / LISTS_W, 0);
+	vec2 blockStep = vec2(1.0 / float(LISTS_W), 0);
 	const float C_ID_SCALE = 255.0;
 	blockPos += 0.5 * blockStep;
 
@@ -402,17 +402,17 @@
 #endif
 
 	while (curveIds.x < SENTINEL_STROKES) {
-		float curveId = (curveIds.x + 0.5) / NUM_CURVES;
+		float curveId = (curveIds.x + 0.5) / float(NUM_CURVES);
 
-		vec4 bx = Texel(curves, vec2(0.0 / CURVE_DATA_SIZE, curveId));
-		vec4 by = Texel(curves, vec2(1.0 / CURVE_DATA_SIZE, curveId));
-		vec4 bounds = Texel(curves, vec2(2.0 / CURVE_DATA_SIZE, curveId));
+		vec4 bx = Texel(curves, vec2(0.0 / float(CURVE_DATA_SIZE), curveId));
+		vec4 by = Texel(curves, vec2(1.0 / float(CURVE_DATA_SIZE), curveId));
+		vec4 bounds = Texel(curves, vec2(2.0 / float(CURVE_DATA_SIZE), curveId));
 
 #if LINE_STYLE > 0 && PAINT_ORDER > 0
 		vec2 off = clamp(position, bounds.xy, bounds.zw) - position;
 
 		if (abs(dot(off, axis1)) < LINE_OFFSET) {
-			vec4 roots = Texel(curves, vec2(3.0 / CURVE_DATA_SIZE, curveId));
+			vec4 roots = Texel(curves, vec2(3.0 / float(CURVE_DATA_SIZE), curveId));
 			if (onCurveLine(position, bx, by, roots, LINE_OFFSET * LINE_OFFSET)) {
 #ifdef GPUX_DEBUG
 				if (int(round(curveIds.x)) == debug_curve) {
@@ -487,7 +487,7 @@
 #endif
 
 		while (curveIds.x < SENTINEL_END) {
-			float curveId = (curveIds.x + 0.5) / NUM_CURVES;
+			float curveId = (curveIds.x + 0.5) / float(NUM_CURVES);
 
 			if (checkLine(curveId, position, axis1)) {
 #ifdef GPUX_DEBUG
diff -ru /Users/nikki/Development/tove2d/src/glsl/line.vert.inc ./glsl/line.vert.inc
--- /Users/nikki/Development/tove2d/src/glsl/line.vert.inc	2020-01-12 19:34:59.000000000 -0800
+++ ./glsl/line.vert.inc	2020-01-12 22:49:33.000000000 -0800
@@ -39,7 +39,7 @@
 }*/
 
 vec2 normal(vec4 bx, vec4 by, float t) {
-	vec3 c_dt = vec3(3 * t * t, 2 * t, 1);
+	vec3 c_dt = vec3(3.0f * t * t, 2.0f * t, 1);
 	vec2 tangent = vec2(dot(bx.xyz, c_dt), dot(by.xyz, c_dt));
 	if (dot(tangent, tangent) < 1e-2) {
 		tangent = at(bx, by, t + 1e-2) - at(bx, by, t - 1e-2);

nikki93 avatar Jan 13 '20 07:01 nikki93

Ok, all seems to work on iOS! Will try Android. I can post a patch for the C++ code and Lua code then do just sort of "build instructions" since I'm mainly just building and linking it into a codebase that has Love among other native libraries.

DC67D387-9C09-474D-BF49-8F7F9CBC8457

nikki93 avatar Jan 13 '20 14:01 nikki93

Oh gosh, this is gorgeous.

poke1024 avatar Jan 13 '20 18:01 poke1024

Have it working on Android too! I build it into a larger project that I've been working on which includes Love and other libraries for the actual build process. You can see what the project files for that look like here under the 'ios' and 'android' subdirectories. Specifically this commit adds building with tove2d for iOS and this one for Android. I applied the diffs shown above on generated code to get the shaders working on mobile--I'd probably work on making it more upstreamable if I stick with tove2d / there is interest (currently just trying it out).

nikki93 avatar Jan 14 '20 05:01 nikki93

This is super neat! I'm working on a game, mainly targeted at iOS, which contains a partial part of the 2.5D engine zDog, which relies on animated paths. The performance with the stock API was pretty bad. Looks like this really is feasible now! Now if Love2D would finally add support for tvOS dreams ...

turbo avatar Jan 14 '20 14:01 turbo

My dream is adding browser support to Löve2D, this will require all libraries to be rebuilt with WASM.

tpimh avatar Nov 20 '20 14:11 tpimh