I'm using ShapeRenderer to draw some kind of circular indicator under a mask texture. Everything works perfectly and as expected on Desktop but when running the same code on android, the shape rendering is always on top. Another strange difference is that all shape rendering function calls seems inverted such the first shape is drawn on top.
I've reproduced the problem on a june 2020 libgdx nightly build and with the 20/09/2020 nightly build.
Here is the code :
myConstructor(){
sr = new ShapeRenderer();
sr.scale(1 + 1.1f * GUI.ZOOM_RATIO, 1 + 1.1f * GUI.ZOOM_RATIO, 0f);
}
draw(Batch g,float arg){
g.end(); // I have some batch rendering before
sr.begin(ShapeType.Filled);
sr.setColor(Color.CYAN);
sr.circle(indX, indY, indicatorW + GUI.fit2Density(2));
sr.setColor(Color.DARK_GRAY);
sr.arc(indX, indY, indicatorW, 0f, degrees);
sr.end();
g.begin();
g.setColor(1f, 1f, 1f, 1f);
g.draw(coolDownIndic, indX - coolDownIndic.getWidth() / 2 - GUI.fit2Density(2),
indY - coolDownIndic.getHeight() / 2);
}
On Desktop I see the texture rendred over the arc and the arc over the circle. This order is exactly inverted on android, you can see the expected behavior here Desktop rendering and the incorrect behavior here Android rendering ( the purple cloud on top is a particle emitter effect not linked to the issue).
I guess this is a bug, note that I'm using the scale method on the ShapeRenderer, not sure if it can be related.
Any help would be appreciated.
Related
I think that I must be misunderstanding how the Renderscript intrinsic to apply a colormatrix works, because my results don't turn out as I expect.
So I have an allocation for Renderscript which "overlays" an OpenCV Mat, basically imagine it as a 3 dimensional array full of pixels where every pixel has RGBA (Red,Green,Blue,Alpha) values.
So I want to apply a colormatrix to every pixel like this:
Vector R times Matrix 0.152286f, 1.052583f, -0.204868f, 0f,
G 0.114503f, 0.786281f, 0.099216f, 0f,
B -0.003882f, -0.048116f, 1.051998f, 0f,
A 0.000000f, 0.000000f, 0.000000f, 1f
So what I expect to happen is that the new Vector R'G'B'A' will be like this:
R' = R * 0.152286f + G * 1.052583f + B * -0.204868f + A * 0f
G' = as above
B' = as above
A' = R * 0 + G * 0 + B * 0 + A * 1
So the new R' value will be a combination of the old RGB values, A does not affect RGB. Same behavior for G' and B'. A will always stay the same.
In Code it looks like this:
Matrix4f mProtan = new Matrix4f(new float[]{
0.152286f, 1.052583f, -0.204868f, 0f,
0.114503f, 0.786281f, 0.099216f, 0f,
-0.003882f, -0.048116f, 1.051998f, 0f,
0.000f, 0.000f, 0.000f, 1f
});
scriptIntrinsicColorMatrix.setColorMatrix(mProtan);
scriptIntrinsicColorMatrix.forEach(inputAllocation, outputAllocation);
So I am already doing this with OpenCV and it works as expected, but it's kinda slow, hence I'd like to use Renderscript, but there my outcome is usually weird, for example this matrix shouldn't really affect anything other than Red, Green and combinations of them (e.g. Red becomes a dark shade of grey/brown, Green becomes a muddy yellow and Purple is Red + Blue so Red goes away and purple becomes only Blue. Even white paper gets a greenish tint).
I also tried direct streaming from the camera feed via Renderscript only and storing information in Bitmaps, but the results end up the same.
Any help would be greatly appreciated! :-)
RS uses column-major format for matrices, so you need to transpose your matrix to get your expected R'G'B'A' values.
https://developer.android.com/guide/topics/renderscript/reference/rs_matrix.html
https://android.googlesource.com/platform/frameworks/rs/+/android-7.0.0_r6/cpu_ref/rsCpuIntrinsicColorMatrix.cpp#820
I am writing a RTS Game for Android and I want the "Fog of War" effect on the player's units. This effect means that only a "circle" around each unit shows the background map while on places where no player unit is located, the screen should be black. I don't want to use shaders.
I have a first version of it working. What I am doing is to render the map to the default framebuffer, then I have a second Framebuffer (similar to light technics) which is completely black. Where the units of the players are, I then batch-draw a texture which is completely transparent and has a white circle with blurred edges in its middle.
Finally I draw the second (light) FrameBuffer's colorTexture over the first one using Gdx.gl.glBlendFunc(GL20.GL_DST_COLOR, GL20.GL_ZERO);
The visual effect now is that indeed the whole map is black and a circle around my units is visible - but a lot of white color is added.
The reason is pretty clear as I drew the light textures for the units like this:
lightBuffer.begin();
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE);
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.setColor(1f, 1f, 1f, 1f);
for (RTSTroopAction i : unitList) {
batch.draw(lightSprite, i.getX() + (i.getWidth() / 2) - 230, i.getY() + (i.getHeight() / 2) - 230, 460, 460); //, 0, 0, lightSprite.getWidth(), lightSprite.getHeight(), false, true);
}
batch.end();
lightBuffer.end();
However, I don't want the "white stuff" on the original texture, I just want the original background shine through. How can I achieve that ?
I think it's playing around with the blendFuncs, but I was not able to figure out which values to use yet.
Thanks to Tenfour04 pointing into the right direction, I was able to find the solution. First of all, the problem is not directly within batch.end();. The problem is, that indeed a sprite batch maintains its own blendFunc Settings. These get applied when flush(); is called. (end() calls it also ).
However the batch is also calling flush when it draws a TextureRegion that is bound to a different texture than the one used in the previous draw() call.
So in my original code: whatever blendFunc I had set was always overridden when I called batch.draw(lightBuffer,...). The solution is to use the spritebatch's blendFunc and not the Gdx.gl.blendFunc.
The total working code finally looks like this:
lightBuffer.begin();
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glEnable(GL20.GL_BLEND);
// start rendering to the lightBuffer
// set the ambient color values, this is the "global" light of your scene
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// start rendering the lights
batch.setProjectionMatrix(camera.combined);
batch.begin();
// set the color of your light (red,green,blue,alpha values)
batch.setColor(1f, 1f, 1f, 1f);
for (RTSTroopAction i : unitList) {
if (i.getOwnerId() == game.getCallback().getPlayerId()) {
batch.draw(lightSprite, i.getX() + (i.getWidth() / 2) - 230, i.getY() + (i.getHeight() / 2) - 230, 460, 460); //, 0, 0, lightSprite.getWidth(), lightSprite.getHeight(), false, true);
}
}
batch.end();
lightBuffer.end();
// now we render the lightBuffer to the default "frame buffer"
// with the right blending !
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
batch.setProjectionMatrix(getStage().getCamera().combined);
batch.enableBlending();
batch.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
batch.begin();
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
batch.draw(lightBufferRegion,0, 0, getStage().getWidth(), getStage().getHeight());
batch.end();
batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
I'm pretty much looking for exactly what the question suggests - a DecelerateAccelerateInterpolator. What I want to do is have an animation decelerate for the first half of the animation, and then accelerate after that. (I'm using it to mimic a gravity-like effect on a Bèzier curve).
EDIT:
Basically, what I'm looking for is something that as the object moves upward on the screen along a Bèzier curve, it decelerates until it gets to the top (at which point it momentarily stops, or has 0 speed or whatever), and then it starts to accelerate as it travels back down the other side.
If you're using Android API 21 or greater you can use PathInterpolator with a cubic Bezier curve:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
TimeInterpolator lInter = new PathInterpolator(0.0f, 0.97f, 1.0f, 0.03f);
animation.setInterpolator(lInter);
}
And here's a great website for calculating your desired bezier curve values:
Try to use my custom Interpolator. And you're welcome :)
mAnimation.setInterpolator(new Interpolator() {
#Override
public float getInterpolation(float pInput) {
System.out.println("input " + pInput);
// (1-(1-(x*2))^3)/2
return (float) (1 - Math.pow(1 - (2 * pInput), 3)) / 2;
}
});
I'm thinking you could actually chain two Interpolators. The first would be a DecelerateInterpolator from time 0 to time 1, then start an AccelerateInterpolator at time 1 to time 2. Basically split the time in half.
iOS Code:
I have working code on iOS which prepares a 3D transformation for a UIView's layer:
CATransform3D t = CATransform3DIdentity;
t.m34 = -1.0f/500.0f;
t = CATransform3DTranslate(t, 10.0f, 20.0f, 0.0f);
t = CATransform3DRotate(t, 0.25f * M_PI, -1.0f, 0.0f, 0.0f);
I'm trying to port the above code to Android. I'm trying to prepare an android.view.animation.Transformation t which will do the same thing. It will be executed by ViewGroup.getChildStaticTransformation(View v, Transformation t).
unfinished Android Code:
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX);
android.graphics.Camera camera = new android.graphics.Camera();
// set perspective (m34) here.. how??
camera.translate(10.0f, 20.0f, 0.0f);
camera.rotateX(-1.0f * Math.toDegrees(0.25 * Math.PI));
camera.getMatrix(t.getMatrix());
My main issue:
The main problem is that I'm not sure how set the perspective t.m34 = -1.0f/500.0f in Android. Reading the docs is rather cryptic and my best bet is using Camera.setLocation(). Also, the docs say nothing about units, so what would be an appropriate value?
Another issue is that setLocation() is only available from API 12, so I would really need to set it manually in the Matrix instead (or via some transformation). Any ideas how?
Final comment:
I'm aware that there are probably more issues.. like the translate() units, transformation order and generally the issue that we transform the camera in Android but the object in iOS. I will get to all of these later :)
I am porting some code to Android from Visual C++. The VC++ ArcTo function takes the bounding rectangle and the start and end points as parameters to define the arc. The android.graphics.Path function arcTo takes the bounding rectangle and the "start angle" and "sweep angle" as parameters.
I am not clear how to convert from the VC set of coordinates to the Android set, or what these two angles are. The arc also has direction (CW or ACW) - I am not clear how to incorporate these in a single Path, or how to switch between one and the other.
One oddity I came across is that in the Android function, angles are expressed in degrees, rather than radians which is what most calculations would use and what one would expect.
I hope my question makes some sort of sense and that someone can help!
Edit: following on from the help I got from Dr Dredel, and with much drawing of diagrams, here's how I eventually translated the VC++ call to Android:
else if (coord.isArc())
{
ptCentre = getPoint(new Coord(coord.getArcLat(), coord.getArcLong()));
nRadius = getPixels(coord.getArcRadius());
rect = new RectF(ptCentre.x - nRadius, ptCentre.y - nRadius,
ptCentre.x + nRadius, ptCentre.y + nRadius);
if (coord.isClockwise())
{
alpha = Math.atan2(ptCentre.y - ptStart.y, ptCentre.x - ptStart.x) *
Constants.k_d180Pi;
beta = Math.atan2(ptCentre.y - ptEnd.y, ptEnd.x - ptCentre.x) *
Constants.k_d180Pi;
path.arcTo(rect, (float)(alpha + 180), (float)(180 - beta - alpha));
}
else
{
}
As you can see, I haven't done the anti-clockwise arc yet, but it should be similar. My calculation wasn't perfect, as I originally had (360 - beta - alpha) instead of (180 - beta - alpha), and the original version gave some very funny results!
(Wow! this formatting mechanism is the other side of weird!)