(Unity3D) Paint with soft brush (logic) - unity3d

During the last few days i was coding a painting behavior for a game am working on, and am currently in a very advanced phase, i can say that i have 90% of the work done and working perfectly, now what i need to do is being able to draw with a "soft brush" cause for now it's like am painting with "pixel style" and that was totally expected cause that's what i wrote,
my current goal consist of using this solution :
import a brush texture, this image
create an array that contain all The alpha values of that texture
When drawing use the array elements in order to define the new pixels alpha
And this is my code to do that (it's not very long, there is too much comments)
//The main painting method
//theObject = the object to be painted
//tmpTexture = the object current texture
//targetTexture = the new texture
void paint (GameObject theObject, Texture2D tmpTexture, Texture2D targetTexture)
{
//x and y are 2 floats from another class
//they store the coordinates of the pixel
//that get hit by the RayCast
int x = (int)(coordinates.pixelPos.x);
int y = (int)(coordinates.pixelPos.y);
//iterate through a block of pixels that goes fro
//Y and X and go #brushHeight Pixels up
// and #brushWeight Pixels right
for (int tmpY = y; tmpY<y+brushHeight; tmpY++) {
for (int tmpX = x; tmpX<x+brushWidth; tmpX++) {
//check if the current pixel is different from the target pixel
if (tmpTexture.GetPixel (tmpX, tmpY) != targetTexture.GetPixel (tmpX, tmpY)) {
//create a temporary color from the target pixel at the given coordinates
Color tmpCol = targetTexture.GetPixel (tmpX, tmpY);
//change the alpha of that pixel based on the brush alpha
//myBrushAlpha is a 2 Dimensional array that contain
//the different Alpha values of the brush
//the substractions are to keep the index in range
if (myBrushAlpha [tmpY - y, tmpX - x].a > 0) {
tmpCol.a = myBrushAlpha [tmpY - y, tmpX - x].a;
}
//set the new pixel to the current texture
tmpTexture.SetPixel (tmpX, tmpY, tmpCol);
}
}
}
//Apply
tmpTexture.Apply ();
//change the object main texture
theObject.renderer.material.mainTexture = tmpTexture;
}
Now the fun (and bad) part is the code did exactly what i asked for, but there is something that i didn't think of and i couldn't solve after spend the whole night trying,
the thing is that by asking to draw anytime with the brush alpha i found myself create a very weird effect which is decreasing the alpha value of an "old" pixel, so i tried to fix that by adding an if statement that check if the current alpha of the pixel is less than the equivalent brush alpha pixel, if it is, then augment the alpha to be equal to the brush, and if the pixel alpha is bigger, then keep adding the brush alpha value to it in order to have that "soft brushing" effect, and in code it become this :
if (myBrushAlpha [tmpY - y, tmpX - x].a > tmpCol.a) {
tmpCol.a = myBrushAlpha [tmpY - y, tmpX - x].a;
} else {
tmpCol.a += myBrushAlpha [tmpY - y, tmpX - x].a;
}
But after i've done that, i got the "pixelized brush" effect back, am not sure but i think maybe it's because am making these conditions inside a for loop so everything is executed before the end of the current frame so i don't see the effect, could it be that ?
Am really lost here and hope that you can put me in the right direction,
Thank you very much and have a great day

Related

Using compute shader to return whether texture has certain colour

I'm writing a Compute Shader (in the unity environment, which uses DirectX11 DirectCompute) which I need to do a very simple task: check whether any pixel in the image has green == 1 and blue > 0.5. (For clarity - the green channel is a 2d "light" and the blue channel is that lights "trail" - I want to detect whenever the light crosses back over its trail.)
I'm as far as displaying the overlap (shown in white) for debugging purposes, but I have no idea how to do something as simple as return a value indicating whether the texture actually contains an overlap. My confusion stems from how threads work. I have a float buffer with room for a single float - I simply need a 1 or 0.
For clarification the following two images show a "before" and "after" - all I need is a single "true" value telling me that some white exists in the second image.
The compute shader is as follows:
#pragma kernel CSMain
Texture2D<float4> InputTexture;
RWTexture2D<float4> OutputTexture;
RWStructuredBuffer<float> FloatBuffer;
[numthreads(8,8,1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
// need to detect any region where g == 1 and blue > 0.5
float green = InputTexture[id.xy].g;
float blue = round(InputTexture[id.xy].b);
float overlap = round((green + blue) / 2.0);
OutputTexture[id.xy] = float4(overlap, overlap, overlap, 1);
// answer here?? Note that the output texture is only for debugging purposes
FloatBuffer[0] = ??
}
You have the option of using atomic operation and count the pixels. You run your compute shader with one thread per pixel, and if the pixel meet the criteria, increment your rwbuffer.
Something like this :
Texture2D<float4> InputTexture;
RWBuffer<uint> NotAFloatBuffer;
[numthreads(8,8,1)]
void CSMain(uint3 id : SV_DispatchThreadID {
// need to detect any region where g == 1 and blue > 0.5
float green = InputTexture[id.xy].g;
float blue = round(InputTexture[id.xy].b);
float overlap = round((green + blue) / 2.0);
if (overlap > 0)
InterlockedAdd(NotAFloatBuffer[0],1);
}
In your case, you can stop here but atomics have some little cost penalties and often they are optimized by grouping the call from one single thread in your group with prior reduction, but this is only in the most extreme cases, you do not have to worry about that.

SpriteKit : calculate distance between two texture masks

I have two irregular shapes in SpriteKit, and I want to calculate the vertical distance from the base of a space ship and the (irregular) terrain right below.
Is there a way to do it ?
Thanks !
Place an SKPhysicsBody that is in a shape of a line at the center of your ship with a width of 1 and the height of your scene, then in the didBeginContact method, grab the 2 contact points. You now know 2 points, just use the distance formula (in this case it is just y2-y1) and you have your answer
I found a different way to solve my problem, but I think that KnightOfDragon's one is conceptually better (although I did not manage to make it work).
The terrain's texture is essentially a bitmap with opaque and transparent pixels. So I decided to parse these pixels, storing the highest opaque pixel for each column, building a "radar altitude map". So I just have to calculate the difference between the bottom of the ship and the altitude of the column right beneath its center:
CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(terrain.texture.CGImage));
const UInt32 *pixels = (const UInt32*)CFDataGetBytePtr(imageData);
NSMutableArray *radar = [NSMutableArray new];
for (long col = 0; col < terrain.size.width; col++)
[radar addObject:#(0)];
for (long ind = 0; ind < (terrain.size.height * terrain.size.width); ind++)
{
if (pixels[ind] & 0xff000000) // non-transparent pixel
{
long line = ind/terrain.size.width;
long col = ind - (line*terrain.size.width);
if ([radar[col]integerValue] <terrain.size.height-line) radar[col] = #(terrain.size.height-line);
}
}
This solution could be optimized, of course. It's just the basic idea.
I've added an image to show the original texture, its representation as opaque/transparent pixels, and a test by putting little white nodes to check where the "surface" was.

Gradient not rendering correctly on a Unity3D GUI object

I am attempting to apply a gradient effect on a Unity3D(5.2) gui object but its as if one of the gradient color keys is being completely ignored. I have tried with both instantiating a new gradient field and declaring a gradient field public and edit its keys in the editor but yet the effects remain the same.
I'm beginning to think that I am not supposed to use Gradients in a BaseMeshEffect in the way I am using it. If only have 2 keys, the colors render properly. Where am I wrong?
Here is a code sample followed by a screen shot.
public class GradientUI : BaseMeshEffect
{
[SerializeField]
public Gradient Grad;
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive())
{
return;
}
List<UIVertex> vertexList = new List<UIVertex>();
vh.GetUIVertexStream(vertexList);
ModifyVertices(vertexList);
vh.Clear();
vh.AddUIVertexTriangleStream(vertexList);
}
void ModifyVertices(List<UIVertex> vertexList)
{
int count = vertexList.Count;
float bottomY = vertexList[0].position.y;
float topY = vertexList[0].position.y;
for (int i = 1; i < count; i++)
{
float y = vertexList[i].position.y;
if (y > topY)
{
topY = y;
}
else if (y < bottomY)
{
bottomY = y;
}
}
float uiElementHeight = topY - bottomY;
for (int i = 0; i < count; i++)
{
UIVertex uiVertex = vertexList[i];
float percentage = (uiVertex.position.y - bottomY) / uiElementHeight;
// Debug.Log(percentage);
Color col = Grad.Evaluate(percentage);
uiVertex.color = col;
vertexList[i] = uiVertex;
Debug.Log(uiVertex.position);
}
}
Screen shot
Your script is actually OK, no problem with it. The problem here is that UI elements simply don't have enough geometry for you to actually see the whole gradient.
Let me explain. In a nutshell, each UI element is actually a mesh made of several 3D triangles, each one rotated to face the camera exactly with its front so it looks 2D. You filter works by assigning a color value to each vertex of those triangles. The color values in the middle of triangles are interpolated based on the proximity to each of the colored vertices.
If you look at UI element in wireframe, you will see that its geometry is very simple. This is for example how a sliced image looks:
As you can see, all of its vertices are concentrated at the corners, and there are no vertices in the middle. So, lets assume your gradient is 2 keys WHITE=>RED. The upper vertices get value WHITE or close to WHITE, the bottom values get value RED or close to RED. This works OK for 2 keys.
Now assume you have 3 keys WHITE=>BLUE=>RED. The upper value is WHITE or close to WHITE, the bottom values get value RED or close to RED, the BLUE value is supposed to be somewhere in the middle, but there is no vertex in the middle, so it is not assigned to anything. So you still get WHITE to RED gradient.
Now, what you can do:
1) You can add some more geometry programmatically, for example by simply subdividing the whole mesh. This may help you: http://answers.unity3d.com/questions/259127/does-anyone-have-any-code-to-subdivide-a-mesh-and.html. Pay attention that in this case, the more keys your gradient has, the more subdivisions are required.
2) Use texture that looks like a gradient gradient.

How to move incrementally in a 3D world using glRotatef() and glTranslatef()

I have some 3D models that I render in OpenGL in a 3D space, and I'm experiencing some headaches in moving the 'character' (that is the camera) with rotations and translation inside this world.
I receive the input (ie the coordinates where to move/the dregrees to turn) from some extern event (image a user input or some data from a GPS+compass device) and the kind of event is rotation OR translation .
I've wrote this method to manage these events:
- (void)moveThePlayerPositionTranslatingLat:(double)translatedLat Long:(double)translatedLong andRotating:(double)degrees{
[super startDrawingFrame];
if (degrees != 0)
{
glRotatef(degrees, 0, 0, 1);
}
if (translatedLat != 0)
{
glTranslatef(translatedLat, -translatedLong, 0);
}
[self redrawView];
}
Then in redrawView I'm actualy drawing the scene and my models. It is something like:
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
NSInteger nModels = [models count];
for (NSInteger i = 0; i < nModels; i++)
{
MD2Object * mdobj = [models objectAtIndex:i];
glPushMatrix();
double * deltas = calloc(sizeof(double),2);
deltas[0] = currentCoords[0] - mdobj.modelPosition[0];
deltas[1] = currentCoords[1] - mdobj.modelPosition[1];
glTranslatef(deltas[0], -deltas[1], 0);
free(deltas);
[mdobj setupForRenderGL];
[mdobj renderGL];
[mdobj cleanupAfterRenderGL];
glPopMatrix();
}
[super drawView];
The problem is that when translation an rotation events are called one after the other: for example when I'm rotating incrementally for some iterations (still around the origin) then I translate and finally rotate again but it appears that the last rotation does not occur around the current (translated) position but around the old one (the old origin). I'm well aware that this happens when the order of transformations is inverted, but I believed that after a drawing the new center of the world was given by the translated system.
What am I missing? How can I fix this? (any reference to OpenGL will be appreciated too)
I would recommend not doing cummulative transformations in the event handler, but internally storing the current values for your transformation and then only transforming once, but I don't know if this is the behaviour that you want.
Pseudocode:
someEvent(lat, long, deg)
{
currentLat += lat;
currentLong += long;
currentDeg += deg;
}
redraw()
{
glClear()
glRotatef(currentDeg, 0, 0, 1);
glTranslatef(currentLat, -currentLong, 0);
... // draw stuff
}
It sounds like you have a couple of things that are happening here:
The first is that you need to be aware that rotations occur about the origin. So when you translate then rotate, you are not rotating about what you think is the origin, but the new origin which is T-10 (the origin transformed by the inverse of your translation).
Second, you're making things quite a bit harder than you really need. What you might want to consider instead is to use gluLookAt. You essentially give it a position within your scene and a point in your scene to look at and an 'up' vector and it will set up the scene properly. To use it properly, keep track of where you camera is located, call that vector p, and a vector n (for normal ... indicates the direction you're looking) and u (your up vector). It will make things easier for more advanced features if n and u are orthonormal vectors (i.e. they are orthoginal to each other and have unit length). If you do this, you can compute r = n x u, (your 'right' vector), which will be a normal vector orthoginal to the other two. You then 'look at' p+n and provide the u as the up vector.
Ideally, your n, u and r have some canonical form, for instance:
n = <0, 0, 1>
u = <0, 1, 0>
r = <1, 0, 0>
You then incrementally accumulate your rotations and apply them to the canonical for of your oritentation vectors. You can use either Euler Rotations or Quaternion Rotations to accumulate your rotations (I've come to really appreciate the quaternion approach for a variety of reasons).

Car turning circle and moving the sprite

I would like to use Cocos2d on the iPhone to draw a 2D car and make it steer from left to right in a natural way.
Here is what I tried:
Calculate the angle of the wheels and just move it to the destination point where the wheels point to. But this creates a very unnatural feel. The car drifts half the time
After that I started some research on how to get a turning circle from a car, which meant that I needed a couple of constants like wheelbase and the width of the car.
After a lot of research, I created the following code:
float steerAngle = 30; // in degrees
float speed = 20;
float carWidth = 1.8f; // as in 1.8 meters
float wheelBase = 3.5f; // as in 3.5 meters
float x = (wheelBase / abs(tan(steerAngle)) + carWidth/ 2);
float wheelBaseHalf = wheelBase / 2;
float r = (float) sqrt(x * x + wheelBaseHalf * wheelBaseHalf);
float theta = speed * 1 / r;
if (steerAngle < 0.0f)
theta = theta * -1;
drawCircle(CGPointMake(carPosition.x - r, carPosition.y),
r, CC_DEGREES_TO_RADIANS(180), 50, NO);
The first couple of lines are my constants. carPosition is of the type CGPoint. After that I try to draw a circle which shows the turning circle of my car, but the circle it draws is far too small. I can just make my constants bigger, to make the circle bigger, but then I would still need to know how to move my sprite on this circle.
I tried following a .NET tutorial I found on the subject, but I can't really completely convert it because it uses Matrixes, which aren't supported by Cocoa.
Can someone give me a couple of pointers on how to start this? I have been looking for example code, but I can't find any.
EDIT After the comments given below
I corrected my constants, my wheelBase is now 50 (the sprite is 50px high), my carWidth is 30 (the sprite is 30px in width).
But now I have the problem, that when my car does it's first 'tick', the rotation is correct (and also the placement), but after that the calculations seem wrong.
The middle of the turning circle is moved instead of kept at it's original position. What I need (I think) is that at each angle of the car I need to recalculate the original centre of the turning circle. I would think this is easy, because I have the radius and the turning angle, but I can't seem to figure out how to keep the car moving in a nice circle.
Any more pointers?
You have the right idea. The constants are the problem in this case. You need to specify wheelBase and carWidth in units that match your view size. For example, if the image of your car on the screen has a wheel base of 30 pixels, you would use 30 for the WheelBase variable.
This explains why your on-screen circles are too small. Cocoa is trying to draw circles for a tiny little car which is only 1.8 pixels wide!
Now, for the matter of moving your car along the circle:
The theta variable you calculate in the code above is a rotational speed, which is what you would use to move the car around the center point of that circle:
Let's assume that your speed variable is in pixels per second, to make the calculations easier. With that assumption in place, you would simply execute the following code once every second:
// calculate the new position of the car
newCarPosition.x = (carPosition.x - r) + r*cos(theta);
newCarPosition.y = carPosition.y + r*sin(theta);
// rotate the car appropriately (pseudo-code)
[car rotateByAngle:theta];
Note: I'm not sure what the correct method is to rotate your car's image, so I just used rotateByAngle: to get the point across. I hope it helps!
update (after comments):
I hadn't thought about the center of the turning circle moving with the car. The original code doesn't take into account the angle that the car is already rotated to. I would change it as follows:
...
if (steerAngle < 0.0f)
theta = theta * -1;
// calculate the center of the turning circle,
// taking int account the rotation of the car
circleCenter.x = carPosition.x - r*cos(carAngle);
circleCenter.y = carPosition.y + r*sin(carAngle);
// draw the turning circle
drawCircle(circleCenter, r, CC_DEGREES_TO_RADIANS(180), 50, NO);
// calculate the new position of the car
newCarPosition.x = circleCenter.x + r*cos(theta);
newCarPosition.y = circleCenter.y + r*sin(theta);
// rotate the car appropriately (pseudo-code)
[car rotateByAngle:theta];
carAngle = carAngle + theta;
This should keep the center of the turning circle at the appropriate point, even if the car has been rotated.