var ray_basis = global_transform.looking_at(direction)
Is this what you mean? direction = linear_velocity.normalized()
var ray_basis = global_transform.looking_at(direction)
Is this what you mean? direction = linear_velocity.normalized()
Best to use a fresh Transform3D object so you don’t need to deal with positions that are stored in global_transform
So either Transform3D().looking_at(velocity) or use the static basis function Basis.looking_at(velocity). The latter will return the basis directly.
In both cases you don’t even need to normalize the velocity. It wouldn’t make any difference.
Thanks for confirming. I understand why it doesn’t make a difference but I’m trying to understand what I’m doing, and in my head its just easier with 0→1 numbers.
So with var ray_basis = Basis.looking_at(direction) the basis is transformed so -z points along the direction of the velocity.
We rotate on x and z axis. I presume that is because looking_at() used (0, 1, 0) as the up vector, so y can’t be used?
How do I figure out how much to rotate it by?
My current centre point (the red dot on the hemisphere) would be along (1, 0, 0) world space. To rotate it to face along -z (0,0,-1) that should be a -90 deg on z then 90 deg on x. Does that make sense?
That’s fine.
Yes.
I made a typo there. We rotate around x and y basis vectors. We could rotate around z but then we’d need to rotate the whole basis. For the sake of this discussion let’s keep it as x and y. We rotate around those axes because the vector we rotate coincide with the z axis, so rotating around that wouldn’t have any effect.
Well if you want to cover the hemisphere then its in range (-90, 90) for both rotations. Make a small mockup scene of this and try to manually play out those rotations. The rotation gizmo must be in world space to simulate what we’re doing here.
Just -90 degs around y
The editor has a debug view checkbox in the center top menu in runtime so you can visualize the raycasts. I always use RayCast3D nodes and they require a target_position that is defined relative to the position, so more like a scaled direction vector. They dont use the target global position. Not sure if thats the problem.
I would ditch the hemisphere math for this task, take the boid running detections, then get the global_basis, use that to construct a rectangular region surrounding the boid - this could encompass the cylinder. Use the rectangle as an orthogonal projection plane.
Then for each boid in the cylinder, get the local rectangular coordinates.
vto = detected.global_position - global_position
Then get the dot product between this vector and your current boids basis vectors, the local basis vectors are called x, y and z
vdots = basis * vto
Then get the projection of the detected boids position v onto the plane of your boid
vproj = vto - (vto.n/(n.n)) n
Where n here is the global_basis.z vector of your current boid, and so its likely that n.n=1.
Then get the current velocity of the target boid and project the point given after one velocity update too.
Next do the same for each boid, then, there must be a million easy ways to choose a vector that doesnt not touch the swept area 2D’s on the plane.
I would look at whether the other boids current velocity is a collision course. Maybe its best to steer / accelerate away from the nearest one, so you could just get the vector between the projection point and the actual point, vW, then take the cross product from vW with the current point projected and the projected point after one velocity update, call is vcross. Now vcross is perpendicular to the other boids velocity, so take the forward dir of the boid and add vcross.normalized() or the negative. Check whether the new vector is towards the other boids using the dot product.
I would only process boids in front. Possible issue here with the planar coordinates being local so you might have to add origin, also boids might have to tilt about the x axis to change direction, so you also get the roll angle - that might change things when the other boid also avoids and so each physics frame would cause a change. Perhaps they could favour the direction of the other boids south pole.
which option is it? I’m using 4.5 and none of the debug tool tips would indicate they will display the ray cast.
hi, thanks for the comment. While I appreciate the suggestion the ray casts are not for boids detecting boids. They are for boids detecting not boids to avoid. The hemisphere expands around the object with a given radius (that I can change in the script as well as adapt the search area). I don’t want to look solely in the cylinder because the ray casts are looking to update motion when something is in the fov of the model which is represented by the cylinder.
I’m going to stick with the maths and approach I currently have. Its almost workable right now and I have an intuitive understanding of it already.
So because the object is oriented along z rotation is kind of pointless on that axis. U’d just be spinning around, like tossing a pizza.
var ray_basis = Basis.looking_at(direction)
# hemishphere co ordinate points
var temp_vect = Vector3(temp_x, temp_y, temp_z)
var temp_vect_norm = temp_vect.normalized()
# rotate temp vect_norm
temp_vect_norm = temp_vect_norm.rotated(ray_basis.y, deg_to_rad(90.0))
# ray cast target location radius = 10.0m, origin = global_position
ray_cast_target = origin + radius * temp_vect_norm
Is this the right way to type it? Cause with +90 the point is always oriented along the positive direction of the axis of motion and -90 is always oriented on the negative axis of motion.
eg if rot = +90 (flips signs on -90)
| Direction of motion | temp_vect_norm |
|---|---|
| 0 0 1 | 1 0 -0 |
| 0 0 -1 | 1 0 -0 |
| 0 1 0 | 0 -1 -0 |
| 0 -1 0 | 0 1 -0 |
| 1 0 0 | 1 0 -0 |
| -1 0 0 | 1 0 -0 |
I’m not sure I follow the rational here. I’d like to discuss this more please.
If I’m looking at the grid system in godot my 0 point on the hemisphere sits on +x. Since the basis has transformed so -z always points along the axis of the linear velocity/direction, that means we want to transform our 3d space so our +x is on the -z axis.
Using the right hand rule, the thumb point upwards along the y axis, is that correct?
if so positive rotation moves from +z →+x → -z →-x →+z , that would lead me to believe the rotation of +90 around the y axis is sufficient.
The basis updates to the direction every time its called so I wouldn’t suspect that of being the cause, otherwise the print out of my ray cast results would stay on +x no matter what. The issue must be my rotation of the vector. I must be missing something. Is rotating using the basis.axis not the same as rotating the grid around the axis?
“Visible collision shapes” in the debug menu at the very top left. These will work for RayCast3D nodes and capsules but not for something you just made in code.
Yeah well the projection i described would work for a camera in 3D.
If you are you are using local space coordinates, the simple trick you probably know is:
V = r cos(theta)cos(phi) i + r cos(theta) sin(phi) j + r sin (theta) k
Then you replace the unit vectors i,j,k with unit vectors local_basis.x, local_basis.y, local_basis.z
e.g
local_basis.x = Vector3(x1,x2,x3)
Ok? Sometimes need a reminder myself.
That math is the coordinate transform from spherical coordinates to cartesian coordinates local to the space of the boid, so you need to make sure you have the correct basis. It puts the hemisphere on the boid facing forwards.
But then you might speed up using the cartesian coordinates anyway …
The empty space is just the compliment of the occupied space B = (1 - C)
i think actually you should use voxels in the local space, steer the boid to the unnoccupied voxel and use the gradient \partial d X_i / \partial d t _(voxels)
That means the rate of change of the position vectors in voxel units. Then make large voxels so that you can detect which voxel a boid is going to enter at future time steps really quickly.
Are you just preventing premature optimization? Theres a lot more empty voxels than full ones so processing them takes longer.
What is temp_vect?
Cut the OP some slack. They still struggle to figure out basic coordinate system orientation in 3D space. You’re overwhelming them with all this information from all over the place.
for angle_observation in [0.0]:
for theta in [0.0+90]:
for phi in [90.0]:
# its only on the centre right now
temp_x = cos(deg_to_rad(f_theta))
temp_y = cos(deg_to_rad(f_phi))
temp_z = sin(deg_to_rad(f_theta))
temp_vect = Vector3(temp_x, temp_y, temp_z)
wait a sec i think my dumbass has rotated it without realising
You don’t need to think in terms of spherical coordinates at all. We’re doing this with vectors. It’s simpler and more elegant. Those trig operations will be carried out under the hood by engine’s matrix math.
Forget about basis for a moment. Imagine you have a regular coordinate system and your arrow is pointing in -z direction.
If you want to bring this arrow to point at some point on the -z hemisphere, what do you do? You need to rotate it using two consecutive rotation operations; first to adjust the latitude and second to adjust longitude.
So first you rotate it around the x axis to adjust its latitude. +90 will rotate it to point at the north pole and -90 will rotate it to point at the south pole. You pick some angle between -90 and 90.
The second rotation is around y axis which will adjust the longitude, again in the range -90 (pointing east) and +90 (pointing west)
So to summarize: start with -z vector. Rotate around x axis by some (-90, 90) angle, and then rotate again around y axis by some (-90, 90) angle.
If you do this in a nested loop, iterating both angles, always rotating the same -z vector - you’ll end up with the array of vectors distributed on the whole hemisphere (although not uniformly, but that’s for another topic)
I understand what you mean, let me try and see if I can work out how to set that up. Thank you.
TBH I don’t need a perfect hemisphere its why I was fine with by blocky approximation anyway. I know if I wanted to make it have a better resolution I’d just decrease the step size
Yeah try to make it with this pinched distribution first as it is the simplest way. Once you develop some intuition about this, we can look how those vectors can be uniformly distributed.
I think if the OP was just rusty then the vecs i was explaining make plenty of sense. There is no need to think in terms of rotations. If you are detecting nearby boids, you can just go for distance between them being less than the radius … simple as
if (p2 -p1).length() < radius:
then for many boids it becomes an n-body nightmare unless you use a compute shader, because you would have to check every other boid.
So if you use signals with a defined Area3D this is simplified and the physics engine does the spacial hashing dynamic Bounding Volume Hierarchy or whatver … you get the collision. And with the collision you get the vector position (use the global_position). You can also call on the object to request its velocity and then predict the future position … simple as
var newpos = object.global_postion + object.velocity * dt
then you need the dot product …
var dotprod = (newpos -this.global_position).normalized().dot(this.basis.z)
And check that isnt close to 1.0 …
Anyway looking at a bunch of future collision spheres with direction could make it easier, but then all boids steer so the others might head into the same future position or change their velocity, so you need the classic boid policy of head towards the x of the basis constructed from the forward vector with y as the up vector … or something like that. So they all go left instead crashing head on.
I think they usually try to orient their up vector to the other boids down vector - rotate towards that, and perpendicilar to the 2d components of the velocity.
In the head on case they might both start spinning clockwise and find they reached the goal at just 90 degrees then they both pitch up to avoid the other.(they both go left compared to the original direction).
This has got me thinking so i looked it up …
For steering, just take the angle and use the opposite direction … you just need to take the forward vector and the vector to the other boid vto, construct the plane by taking the cross product vcross, rotate about vcross by the angle between forward and vto, multiplied by delta and a fudge factor for tuning.
Do that for each boid in the local group.
For flocking, just head towards tbe center, its
Sum(v_i, N) /N where v_i is position. So find the vector
var vdir = center - global_position
And multiply by delta and a fudge dactor, add to the direction.
Then for directional flocking, just add up all the directions and divide by the number, multiply by delta and a small fudge factor and add that to the direction too.
Finally just normalize the direction.
Okay So I’ve gotten this far:
# check directly ahead
direction = linear_velocity.normalized()
# basis transform to pust -z axis along velocity
var ray_basis = Basis.looking_at(direction)
# ray_cast direction is along -z axis, transform looking_at will align to -z, paralell to lineal velocity
var ray_cast_direction = Vector3(0, 0, -1)
for angle_observation in [0.0]:
for theta in [0]:
for phi in [270]:
var f_theta = float(theta)
var f_phi = float(phi)
#rotate around x axis by theta, this will tilt up and down theta.x uses basis.x likewise for theta.z basis.z
ray_cast_direction = ray_cast_direction.rotated(ray_basis.z, deg_to_rad(f_theta))
# rotate around y axis by phi, this will tilt clockwise or counter
ray_cast_direction = ray_cast_direction.rotated(ray_basis.y, deg_to_rad(f_phi))
# ray cast target
# take the direction * radius, this gives us meters for a location to use
# the offset by global position = origin, since ray is cast in the objective refrence frame
var ray_cast_target = ray_cast_direction * radius + origin
# take diff, we can print this to to confirm rotation results
var diff = ray_cast_target - origin
## rotation study
# direction (1, 0, 0)
#phi.y 0 0 0 0
#theta.x 0 90 180 270
#result -z -z -z -z -z
# rot.x is doing nothing, not sure why
# dir (1,0,0)
# phi.y 0 0 0 0
# theta.z 0 90 180 270
# result -z -y +z +y
# this is rotating around the x dirction (-1, 0, 0)
# this is going counter clock wise when steps are positive
# this is also not expected
# dir(1, 0, 0)
# phi.y 0 90 180 270
# theta.z 0 0 0 0
# result -z -x +z +x
# this is rotating around the y axi
# clockwise is positive step, this makes sense, I expected this
# ray basis components theta.z = 0 phi.y = 270:
#ray_basis x: (0.0, 0.0, 1.0)
#ray_basis y: (0.0, 1.0, 0.0)
#ray_basis z: (-1.0, 0.0, 0.0)
#ray_cast: (1.0, 0.0, -0.0)
I did some rotation steps to figure out which basis rotation does what. I expected theta.z and theta.x to function differently.
I expected rotation ray_basis.y to to move clockwise around the y axis in global space. Which it seems to do. Since (0,1,0) was the up vector for lookin_at() does tha leave it un-altered in the new basis?
I expected rotation ray_basis.x to rotate around the x direction but it seems to perform what I expected of the basis.z rotation.
Is is this due to the direction (1,0,0) being perpendicular to the forward direction (0,0,-1)?