I have Crane Animation which will pick the sand (through its jaw) and drop it to the truck. I tried to find the way to handle sand in Unity but unfortunately found nothing.
So I made custom sand object (low poly) in max and bring them it Unity3d and apply box collider and rigidbody to them. But as I play the game, my FPS drops and player hardly moving. Profiler tell that physics is so heavy (and it should be as too many colliders with too many rigidbodies). I tried to optimize physics by collision matrix and layer but it didn't improve the performance.
Is this right approach to handle sand interaction with a crane?
You could have the sand be a terrain and use the terrain.SetHeights() Method to lower a specific area of the terrain where you used the crane.
It would look like those classic terrain editing tools when you lower or heigher terrain.
Then wherever you "drop" off the sand you could heigher the area a bit.
I found this thread with some code examples to help you get going:
https://forum.unity.com/threads/edit-terrain-in-real-time.98410/
private void raiseTerrain(Vector3 point)
{
int terX =(int)((point.x / myTerrain.terrainData.size.x) * xResolution);
int terZ =(int)((point.z / myTerrain.terrainData.size.z) * zResolution);
float[,] height = myTerrain.terrainData.GetHeights(terX - 4,terZ - 4,9,9); //new float[1,1];
for(int tempY = 0; tempY < 9; tempY++)
for(int tempX = 0; tempX < 9; tempX++)
{
float dist_to_target = Mathf.Abs((float)tempY - 4f) + Mathf.Abs ((float)tempX - 4f);
float maxDist = 8f;
float proportion = dist_to_target / maxDist;
height[tempX,tempY] += 0.001f * (1f - proportion);
heights[terX - 4 + tempX,terZ - 4 + tempY] += 0.01f * (1f - proportion);
}
myTerrain.terrainData.SetHeights(terX - 4, terZ - 4, height);
}
Related
I am using Point Cloud Free Viewer to visualize Point Clouds in Unity. It has a script and it parses .off files and creates meshes without triangulating. However, the code creates multiple meshes since its index format is 16bit. I modified the code for utilizing 32 bit format and i have a mesh with 2 million points:
What i want to do is creating a grid like geometry and color this point cloud based on point density. I want to find a rough volume of this point cloud by multiplying differences between max and min x,y,z values and divide this volume into equal boxes. Each of these boxes will be colored based of how many points they contain. I would be happy if someone can offer me a lead. I tried KDTree approach but it is a bit slow since i have 2 million points. I also tried sorting points before creating the mesh but it takes too much time as well. Is there a way to traverse mesh vertices based on the location without visiting all vertices considering they are indexed randomly? I believe i am looking for a solution like mesh.bounds.contains() but i do not know if a method like spatial search exists.
Not really, a full solution, more a hint towards a direction I would pursue: divide your vertex pool into smaller groups first, I.e into cubes (seperate meshes maybe), precalculate this, then you only have to search within a much smaller region, after an initial search for a set of cubes that neighbour (or touch) your region.
It sounds to me like you want an octree.
First, load all of the points into memory (2 million points really isn't that many - assuming doubles, that's 2,000,000 * 3 * 8 bytes ~= 45 MB). While you are parsing the file and loading the points into memory, record the min and max x, y, and z coordinates. You can then build your octree which bounds that volume in N*LogN. Then, for each of your grid volumes, you can very quickly query the tree to get only the points in that region. I'm pretty sure this is the most efficient way to do what you want.
I would suggest checking the quadtree article for its implementation of queryRange to see how this would be done. An octree is just a 3-d implementation of a quadtree, so the underlying code is more or less the same (with each node containing 8 children instead of 4).
For those who might visit this question later i found a really fast solution based on Nico's comment. I am traversing whole points by parsing my scan file using this script
for (int i = 0; i < numPoints; i++)
{
buffer = sr.ReadLine().Split();
points[i] = new Vector3(float.Parse(buffer[0]) , float.Parse(buffer[1]) , -float.Parse(buffer[2]) );
//Finding minX, minY, minZ
if (points[i].x < minX)
minX = points[i].x;
if (points[i].y < minY)
minY = points[i].y;
if (points[i].z < minZ)
minZ = points[i].z;
//Finding maxX, maxY, maxZ
if (points[i].x > maxX)
maxX = points[i].x;
if (points[i].y > maxY)
maxY = points[i].y;
if (points[i].z > maxZ)
maxZ = points[i].z;
}
Here is my and variables i use with itFindPointIndex function.
deltaX = maxX - minX;
deltaY = maxY - minY;
deltaZ = maxZ - minZ;
gridCountX = Mathf.CeilToInt(deltaX / gridSize);
gridCountY = Mathf.CeilToInt(deltaY / gridSize);
gridCountZ = Mathf.CeilToInt(deltaZ / gridSize);
Resolution = gridCountX * gridCountY * gridCountZ;
Histogram = new int[Resolution];
int FindPointIndex(Vector3 point)
{
//Finds the grid index of the point
int index = Mathf.FloorToInt((point.x - minX) / gridSize) + ((Mathf.FloorToInt((point.z - minZ) / gridSize)) * gridCountX)
+ Mathf.FloorToInt((point.y - minY) / gridSize) * gridCountX * gridCountZ;
if (index < 0)
{
index = 0;
}
return index;
}
Then i can traverse the points again to increment index for each of them to see how many points each grid holds like this:
for (int i = 0; i < numPoints; i++)
{
Histogram[FindPointIndex(points[i])]++;
}
At the end using this histogram i can color the point cloud with another loop.
I'm trying to follow a tutorial which is written in a math programming language I'm not familiar with and attempting to convert the tutorial to C# code for Unity 3d.
See here: http://blog.wolfram.com/2011/07/28/how-i-made-wine-glasses-from-sunflowers/
float theta = Mathf.PI * (3 - Mathf.Sqrt(5));
for (int i = 0; i < spawnPoints.Length; i++)
{
float r = (radius / 2) * Mathf.Sqrt(i) / Mathf.Sqrt(points);
float a = theta * i;
Vector3 coords = transform.TransformDirection( new Vector3(Mathf.Cos(a) * r, 0, Mathf.Sin(a) * r) )+transform.position;
spawnPoints[i] = coords;
}
This if course generates the flat phillotaxic arrangement in 2d. I'm trying to modify Y (up) axis for depth (creating the sphere).
I cannot seem to set the Y (up) axis correctly in proportion with i and radius.
Considering the tutorial above, how should I be calculating Y?
The 3D version is called a spherical Fibonacci lattice. This paper gives a nice explanation. This stackoverflow post has more links.
For a screen overlay I am making for a 3-dimensional game, I need to display icons over certain locations in the 3D world. I've managed to get several variables from the game that should make this possible:
Player position (x,y,z)
Camera position (x,y,z)
Point position (x,y,z)
Camera Angle(FOV)
Using these variables I managed to get the right and left edge of the camera view. Using these 2 variables I am able to get the point between 0 and 1 where the icon should be displayed on the x axis. (0 is on the left edge, 1 is right edge) This seems to work quite well, but only if I am aligned to either the X or Z axis, as shown in the following example:
I've been trying to fix this using the following rotation matrix:
[ Math.Cos(angle), -Math.Sin(angle) ]
[ Math.Sin(angle), Math.Cos(angle) ]
What I do is, I put the player position, camera position and the camera edge positions in the matrix with as rotation point the world point. The problem is, as soon as I put the angle amount at 90 degrees, the X and Y are being flipped. I've been trying to find a solution for this for a few days now, but I can't find it, so I hope anyone can push me in the right direction here. Below are a few parts of my code that might help in finding the solution:
float PCDistXZ = (float)Math.Sqrt(deltaPCx * deltaPCx + deltaPCz * deltaPCz); // X/Z distance from the world point to the camera
Point fakeAvatarPos = RotateAround(new Point((int)point.x, (int)point.z), new Point((int)avatar.x, (int)avatar.z), (int)90);
Point fakeCameraPos = RotateAround(new Point((int)point.x, (int)point.z), new Point((int)camera.x, (int)camera.z), (int)90);
double edgeRight = fakeC.X + (Math.Sin(45) * PCDistXZ);
double edgeLeft = fakeC.X - (Math.Sin(45) * PCDistXZ);
float edgeTest_ScreenPositionX = (1 - (float)((edgeRight - P.x) / (edgeRight - edgeLeft))) * screenWidth;
public static Point RotateAround(Point pCenter,Point pPoint, float pAngle)
{
double angle = (pAngle * Math.PI) / 180;
double[,] matrix = new Double[2, 2] {
{ Math.Cos(angle), Math.Sin(angle) },
{ Math.Sin(angle), Math.Cos(angle) }
};
double xOffset = pPoint.X - pCenter.X;
double yOffset = pPoint.Y - pCenter.Y;
Point newPoint = new Point(
(int)(((pPoint.X - xOffset) * matrix[0, 0]) - ((pPoint.Y - xOffset) * matrix[0, 1])),
(int)(((pPoint.X - yOffset) * matrix[1, 0]) + ((pPoint.Y - yOffset) * matrix[1, 1]))
);
newPoint.X += (int)xOffset;
newPoint.Y += (int)yOffset;
return new Point(newPoint.X,newPoint.Y);
}
Note: I've changed the names of some of the variables to more understandable one, so it could be possible that there are inconsistencies in the names.
EDIT: I found out about view- and projection matrices. I might be able to use those to convert the 3D position to screen. I'm not sure if it's possible to make this matrices with the limited information I have though.
In my C# WinForms application I have a picturebox that hosts 2 curves (Resulted from a voltage/current measurement). The X axis is voltage and Y axis is current. The voltage axis is ranged from -5 to 5 but the current axis is a much smaller scale ranged from -10 uA to 10 uA. The task is to see if the second curve is within 10% of the first curve.
For visual inspection I am trying to draw an envelope around the first curve (Blue one). The curve is just a PointF array. At the moment since I have no idea how to draw a correct envelope around the blue curve, I just draw two other curves that are result of X points of the actual curve added and subtracted by 10% of the original curve. Of course this is a bad approach, but atleast for the section of the curve that is noticably vertical, it works. But as soon as the curve is on its non vertical section, this trick does not work anymore, as you can see in the picture below:
Here is the code that I am using to draw the envelope:
public Bitmap DrawEnvelope(double[,] pinData, float vLimit, float iLimit)
{
g = Graphics.FromImage(box);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
PointF[] u = new PointF[pinData.GetLength(0)]; //Up line
PointF[] d = new PointF[pinData.GetLength(0)]; //Down Line
List<PointF> joinedCurves = new List<PointF>();
float posX = xMaxValue * (vLimit / 100);
float minX = posX * -1;
for (int i = 0; i < pinData.GetLength(0); i++)
{
u[i] = new PointF(400 * (1 + (((float)pinData[i, 0]) + minX) / (xMaxValue + vExpand)), 400 * (1 - ((float)pinData[i, 1] * GetInvers((yMaxValue + iExpand)))));
}
for (int i = 0; i < pinData.GetLength(0); i++)
{
d[i] = new PointF(400 * (1 + (((float)pinData[i, 0]) + posX) / (xMaxValue + vExpand)), 400 * (1 - ((float)pinData[i, 1] * GetInvers((yMaxValue + iExpand)))));
}
Pen pengraph = new Pen(Color.FromArgb(50, 0 ,0 ,200), 1F);
pengraph.Alignment = PenAlignment.Center;
joinedCurves.AddRange(u);
joinedCurves.AddRange(d.Reverse());
PointF[] fillPoints = joinedCurves.ToArray();
SolidBrush fillBrush = new SolidBrush(Color.FromArgb(40, 0, 0, 250));
FillMode newFillMode = FillMode.Alternate;
g.FillClosedCurve(fillBrush, fillPoints, newFillMode, 0);
g.Dispose();
return box;
}
The green circles are added by myself, and they indicate the region that the second curve (Red one) is potentially has a difference bigger than 10% from the orginal curve.
Would be nice if someone put me in the right way, what should I look to to achive a nice envelope around original curve?
UPDATE
Because I am so noob I cant find a way to implement the answers given to this question until now, So put a bounty to see if somone can kindly show me atleast a coding approach to this problem.
You could try finding the gradient between each pair of points and calculating two points either side that are on the orthogonal that passes through the midpoint.
You would then have two more lines defined as a set of points that you could use to draw the envelope.
Your best bet is to iterate your point array and to calculate a perpendicular vector to two consecutive points each time (see Calculating a 2D Vector's Cross Product for implementation clues). Project in either direction along these perpendicular vectors to generate the two point arrays of your envelope.
This function generates them roughly using segment midpoints (as long as the point count is high and your offset is not too small it should look ok when plotted):
private void GetEnvelope(PointF[] curve, out PointF[] left, out PointF[] right, float offset)
{
left = new PointF[curve.Length - 1];
right = new PointF[curve.Length - 1];
for (int i = 1; i < curve.Length; i++)
{
PointF normal = new PointF(curve[i].Y - curve[i - 1].Y, curve[i - 1].X - curve[i].X);
float length = (float)Math.Sqrt(normal.X * normal.X + normal.Y * normal.Y);
normal.X /= length;
normal.Y /= length;
PointF midpoint = new PointF((curve[i - 1].X + curve[i].X) / 2F, (curve[i - 1].Y + curve[i].Y) / 2F);
left[i - 1] = new PointF(midpoint.X - (normal.X * offset), midpoint.Y - (normal.Y * offset));
right[i - 1] = new PointF(midpoint.X + (normal.X * offset), midpoint.Y + (normal.Y * offset));
}
}
It all depends on the way you want the envelop to be sized.
You could calculate/guestimate the slope of the curve in each point by calculating the slope to the next point and the slope to the previous point, average these and then calculate a perpendicular vector to the slope.
Add this vector to the point of the curve; this gives you the right-hand edge of the envelop.
Subtract this vector from the point of the curve; this gives you the left-hand edge of the envelop.
This method will fail if the points are too far apart or very sudden changes in the points appear.
This is probably a dumb suggestion. Perhaps instead of drawing the envelope yourself, maybe you could let winforms do it for you. Try drawing the envelope as a line with a pen that has a larger width. Perhaps it might work.
If you look at this msdn example on varying the pen width, you might see what I mean.
http://msdn.microsoft.com/en-us/library/3bssbs7z.aspx
2 (probably incorrect) possibilities.
Do what you did originally to get the pale blue wide area, but also do it in the vertical direction (not just the horizontal)
Do what Dan suggested with a REALLY thick line (in pale blue) then draw it again, then draw the original (thin) line on top of it.
Basically what I'm trying to do is shade a 2D heightmap using a very very basic raycasting system that basically just checks if the ray is intercepted before it should be to shade it. However it's not working correctly and I've been banging my head for several hours now on this so I figured it couldn't hurt to turn it over to you guys, because I think it's probably something either so blindingly obvious that I won't see it or so complex that I'll never wrap my head around it.
I have a map like this:
And the raycasting is giving me this (keep in mind it's just debug colors; red is ray interception, but before intended position (so shading), blue would be ray interception in the correct place (so highlights or just as-is), and yellow means that point had no ray interaction at all before the while loop cut-out).
The result should be with red on backfacing slopes and areas behind large mountains (shadows) and blue on sun-facing slopes (highlights). There should not be any yellow. So this image indicates that either all of the rays are hitting the wrong place, or the rays are being intersected ALWAYS somewhere else before they reach their target, which is impossible.
At this point I highly suspect the problem is with my trig.
Here's the Ray class:
class Ray
{
public Vector2 Position;
public Vector2 Direction; // Think in XZ coordinates for these (they are on a perpendicular plane to the heightmap)
// Angle is angle from horizon (I think), and height is height above zero (arbitrary)
public float Angle, Height;
private TerrainUnit[,] Terrainmap;
private float U, V;
public Ray(ref TerrainUnit[,] Terrainmap, float height, float angle)
{
this.Terrainmap = Terrainmap;
this.Angle = angle;
this.Height = this.V = height;
// Create new straight vector
this.Direction = new Vector2(0, 1);
// Rotate it to the values determined by the angle
this.Direction = Vector2.Transform(Direction, Matrix.CreateRotationX(Angle));
//this.Direction = new Vector2((float)Math.Sin(angle), -(float)Math.Cos(angle));
// Find the horizontal distance of the origin-destination triangle
this.U = V / (float)Math.Tan(Angle);
// Bleh just initialize the vector to something
this.Position = new Vector2(U, V);
}
public void CastTo(int x, int y)
{
// Get the height of the target terrain unit
float H = (float)Terrainmap[x, y].Height;
// Find where the ray would have to be to intersect that terrain unit based on its angle and height
Position = new Vector2(x - U, H + V);
float Z = 1000 * (float)Terrainmap[0, y].Height;
// As long as the ray is not below the terrain and not past the destination point
while (Position.Y > Z && Position.X <= x)
{
// If the ray has passed into terrain bounds update Z every step
if (Position.X > 0) Z = 1000 * (float)Terrainmap[(int)Position.X, y].Height;
Position.X += Direction.X;
Position.Y += Direction.Y;
}
Terrainmap[x, y].TypeColor = Color.Yellow;
if ((int)Position.X == x) Terrainmap[x, y].TypeColor = Color.Blue;
else Terrainmap[x, y].TypeColor = Color.Red;
}
}
Also just as a formality, the function that is casting each ray and how I am calling that:
if (lighting) CastSunRays(1f, MathHelper.PiOver4);
private void CastSunRays(float height, float angle)
{
Ray ray = new Ray(ref Terrainmap, height, angle);
for (int x = 0; x < Width; x++)
for (int y = 0; y < Height; y++)
ray.CastTo(x, y);
}
I ended up using a much simpler approach with Bresenham's Line Algorithm to find the intercept point; I imagine it's much faster and more efficient than the way I was trying to do it would have been.
My guess is that when your Direction vector is applied to Position, it oversteps the lower limit (Position.Y > -1) before it has a chance to hit the surface (Position.Y <= Terrainmap[(int)Position.X, y].Height).
You could try to decrease the lower limit, or re-order your if/while tests.
Another problem might be that the Direction Vector is too large in comparison to your height-range. The distance between two neighboring pixels is 1, while the whole range of height differences is contained in the range (-1,1). This gives a very flat surface from the ray-casters point of view. When the Direction vector is applied to the Position vector is takes a relatively small step over the length, and a relatively large step over the height.
#Maltor: I actually wanted to comment your own answer, but due to my reputation am not currently able to.
I also used the bresenham's line approach and decreased calculation time to 1/10!
A running example of that can be viewed at my github project TextureGenerator-Online.
The terrain tool uses this approach.
See function setTerrainShadow() at tex_terrain.js