Godot: having problems with the raycasting - godot

I am making a sort of map where points are randomly generated. Each point will then detect all the points nearest to it and draw a line there. I have tried making an Area2D around the points which would detect the other points and draw the lines.
This has worked, but only one line is being drawn to one point, even when there are other points intersecting the area. I did not know how to fix this until I thought of the idea to use raycasting instead of an area2d. It would be able to detect all of the points and just generally be more efficient.
However, this is my first time using raycasting, as I am fairly new to coding and game making. I have set up the rays and some code, but it is still not working.
Code:
var point_a = self.get_global_position()
var point_b = 0
var draw_line = false
func _process(delta):
var ray_list = [$RayCast2D]
for rays in ray_list:
if rays.is_colliding() and rays.get_collider().get_parent() == "Location":
point_b = rays.get_collider().get_parent().get_global_position()
draw_line = true
update()
func _draw():
if draw_line:
draw_line(point_a, to_local(point_b), Color(1, 1, 1, 1), 2)
draw_line = false
Any help with this problem would be great.

What I can tell is that both
var point_a = self.get_global_position()
And
point_b = rays.get_collider().get_parent().get_global_position()
Are in global coordinates.
However, you only transform to local point_b:
draw_line(point_a, to_local(point_b), Color(1, 1, 1, 1), 2)
Transform both:
draw_line(to_local(point_a), to_local(point_b), Color(1, 1, 1, 1), 2)
Although the position of the self in its own local coordinantes (i.e. relative to itself) is always the zero vector:
draw_line(Vector2.ZERO, to_local(point_b), Color(1, 1, 1, 1), 2)
Besides that… Read this:
point_b = rays.get_collider().get_parent().get_global_position()
It is the global position of the parent of the collider. Is that what you want?
If you want the position of the collider, it would be:
point_b = rays.get_collider().get_global_position()
And if you want the position where the ray hit, it would be:
point_b = rays.get_collision_point()
And yes, that is global coordinates too.
Addendum: The result of get_parent here:
if rays.is_colliding() and rays.get_collider().get_parent() == "Location":
Should be a Node, right? And a Node is not a String. So it will never be equal to "Location".
Presumably you meant to check the name:
rays.get_collider().get_parent().name == "Location"
An alternative approach would be checking groups. For example:
rays.get_collider().get_parent().is_in_group("Location")
And if you use groups for the Nodes directly, then you don't have to consider their parent:
rays.get_collider().is_in_group("Location")
Yoo can set the groups from the editor in the Node panel (it is next to the inspector by default) on the Groups tab.

Related

Transform3D problems when slicing and using MultiMeshInstance3D

I have a glb from Blender (Sapling Tree) that has a trunk and leaves which import as separate meshes. I am trying to create a multi mesh with this tree and am currently having some strange results that I can't figure out. I have a script attached to one MultiMeshInstance3D that creates the multi mesh for the trunk and another script attached to another MultiMeshInstance3D which creates the Multimesh for the leaves. Since I want the leaves to be Transformed exactly like the trunk I thought I'd position and rotate the trunks first then grab that transform data and just assign it to each instance for the leaves Multimesh. Unfortunately once I apply the transform using multimesh.set_instance_transform() on the leaves the outcome seems to invert the Vertex in the transform.
In my tree trunk script I create the positions using
for i in len(positions):
var position = positions[i]
var basis = Basis(Vector3.UP, 0.0).rotated(Vector3.UP, randi_range(0, 180))
multimesh.set_instance_transform(i, Transform3D(basis, position))
And in my leaves script I take the trunk transform and then apply each leaves instance to each trunk
var transform1 = tree_trunk.multimesh.transform_array
var j = 0
for i in range(0, tree_trunk.count):
var t = transform1.slice(j, j+4)
multimesh.set_instance_transform(i, Transform3D(t[0], t[1], t[2], t[3]))
j += 4
However the result for the trunk and leaves are different in the fact that the leaves don't have the same transform
Here's an example of what happens:
The trunks transform_array index 0 as an example
[(-0.994367, 0, 0.105988), (0, 1, 0), (-0.105988, 0, -0.994367), (-48.50394, 35.99831, 29.6063),...
The leaves transform_array index 0
[(-0.994367, 0, -0.105988), (0, 1, 0), (0.105988, 0, -0.994367), (-48.50394, 35.99831, 29.6063),...
As you can see it inverts the x-axis z value and the z-axis x value but I don't know why. My current fix is to multiply them to fix it and get my leaves on the same rotation as the trunks.
multimesh.set_instance_transform(i, Transform3D(t[0] * Vector3(1, 1, -1), t[1], t[2] * Vector3(-1, 1, 1), t[3]))
The origin seems to be fine it's just the rotation of the leaves that is off.
That fixes the problem but why do they invert in the first place?

Edit SurfaceTool vertices based on coordinates

Godot Version: v4.0-alpha15
I have a terrain being generated using the SurfaceTool and the MeshInstance3D. I am also moving a purple decal acrossed the surface based on the 3D mouse position. Below is a screenshot of what this looks like.
I want to take the 3D mouse position and raise/lower the terrain on an action press. I found the MeshDataTool but am not quite sure if this allows for that and also not completely sure how to convert the 3D mouse position to the corresponding vertices.
At this point I am sort of completely stuck as there's not a whole lot of documentation that I could find that helps.
I appreciate the help in advance!
I was actually able to figure this out using the MeshDataTool as I mentioned in the original post.
I use the Vector3::distance_to method to get any vertex within 3 of the target position, then I use MeshDataTool::set_vertex in combination with Vector3 methods to add and subtract each of those vertex positions.
var target_position = Vector3(0, 0, 0)
var mdt = MeshDataTool.new()
var mesh = terrain_mesh_instance.mesh
mdt.create_from_surface(mesh, 0)
var points = get_vertex_id(mdt, target_position)
for i in points:
if Input.is_action_pressed("shift"):
mdt.set_vertex(i, mdt.get_vertex(i) + Vector3(0, 1 * delta,0))
elif Input.is_key_pressed(KEY_ALT):
var coords = mdt.get_vertex(i)
coords.y = 0
mdt.set_vertex(i, coords)
else:
mdt.set_vertex(i, mdt.get_vertex(i) + Vector3(0, -1 * delta,0))
# This fixes the normals so the shadows work properly
for face in mdt.get_face_count():
var vertex = mdt.get_face_vertex(face, 0)
var normal = mdt.get_face_normal(face)
mdt.set_vertex_normal(vertex, normal)
mesh.clear_surfaces()
mdt.commit_to_surface(mesh)

how can I rotate line with only changing theta?

I was making animation with manim and I tried to make line that one end is fixed, and one end moves along the arc. The way I wanted to solve this problem was to define each point with theta, and let the point move along the arc as it moves. However, even if the setta was changed, the line did not move, so the rotate was used, but the length of the line did not change, so the line deviated from the arc and failed. I wonder how to make the line move smoothly with the size of the angle when theta value is changed to an arbitrary value with theta as a parameter.
from manim import *
import numpy as np
class RotateVector(Scene):
def construct(self):
theta = 1/9*np.pi
semicircle = Arc(angle=PI, radius=4, stroke_color = GREY, arc_center=np.array([0, -1.5, 0]))
bottom_line = Line(start=np.array([-4.0,-1.5,0]), end=np.array([4.0,-1.5,0]), stroke_color = GREY)
dot_A = np.array([-4.0,-1.5,0])
dot_A_text = Text('A', font_size=35).next_to(dot_A, LEFT+DOWN, buff=0.05)
dot_P = np.array([4*np.cos(theta*2),4*np.sin(theta*2)-1.5,0])
dot_P_text = Text('P', font_size=35).next_to(dot_P, RIGHT+UP, buff=0.05)
line_AP = Line(start=np.array([-4.0,-1.5,0]), end=np.array([4*np.cos(2*theta),4*np.sin(2*theta)-1.5,0]), stroke_color = GREY)
self.play(Create(semicircle), Create(bottom_line), Create(dot_A_text))
self.play(Create(line_AP), Create(dot_P_text))
self.wait(3)
Your mobjects do not change because the dependency on Theta is not retained after initializing the mobject -- but it doesn't take too much to make it work!
The solution to your Problem is using a ValueTracker for the value of theta (to easily allow animating changing it) and updater functions for the mobjects you would like to change. In other words:
theta_vt = ValueTracker(theta)
dot_P_text.add_updater(lambda mobj: mobj.next_to([4*np.cos(theta_vt.get_value()*2),4*np.sin(theta_vt.get_value()*2)-1.5,0]))
line_AP.add_updater(lambda mobj: mobj.put_start_and_end_on([-4, 1.5, 0], [4*np.cos(theta_vt.get_value()*2),4*np.sin(theta_vt.get_value()*2)-1.5,0]))
self.play(theta_vt.animate.set_value(2*PI))
You could even avoid the value tracker and use an auxiliary (empty) mobject for positioning:
P = Mobject().move_to([4*np.cos(theta*2),4*np.sin(theta*2)-1.5,0])
and then change the updaters to use the position of P instead of theta, and make the point move along the desired arc. (That point actually is the one that becomes slightly more complicated, while the updaters simplify a bit.)

how to use vtkImageReSlice?

I am trying to use vtkImageReSlicer to extract a 2d slice from a 3d
vtkImageData object. But I can't seem to get the recipe right. Am I doing it right?
I am also a bit confused about ResliceAxes Matrix. Does it represent a cutting plane? If
I move the ReSliceAxes origin will it also move the cutting plane? When I
call Update on the vtkImageReSlicer, the program crashes. But when I don't
call it, the output is empty.
Here's what I have so far.
#my input is any vtkactor that contains a closed curve of type vtkPolyData
ShapePolyData = actor.GetMapper().GetInput()
boundingBox = ShapePolyData.GetBounds()
for i in range(0,6,2):
delta = boundingBox[i+1]-boundingBox[i]
newBoundingBox.append(boundingBox[i]-0.5*delta)
newBoundingBox.append(boundingBox[i+1]+0.5*delta)
voxelizer = vtk.vtkVoxelModeller()
voxelizer.SetInputData(ShapePolyData)
voxelizer.SetModelBounds(newBoundingBox)
voxelizer.SetScalarTypeToBit()
voxelizer.SetForegroundValue(1)
voxelizer.SetBackgroundValue(0)
voxelizer.Update()
VoxelModel =voxelizer.GetOutput()
ImageOrigin = VoxelModel.GetOrigin()
slicer = vtk.vtkImageReslice()
#Am I setting the cutting axis here. x axis set at 1,0,0 , y axis at 0,1,0 and z axis at 0,0,1
slicer.SetResliceAxesDirectionCosines(1,0,0,0,1,0,0,0,1)
#if I increase the z value, will the cutting plane move up?
slicer.SetResliceAxesOrigin(ImageOrigin[0],ImageOrigin[1],ImageOrigin[2])
slicer.SetInputData(VoxelModel)
slicer.SetInterpolationModeToLinear()
slicer.SetOutputDimensionality(2)
slicer.Update() #this makes the code crash
voxelSurface = vtk.vtkContourFilter()
voxelSurface.SetInputConnection(slicer.GetOutputPort())
voxelSurface.SetValue(0, .999)
voxelMapper = vtk.vtkPolyDataMapper()
voxelMapper.SetInputConnection(voxelSurface.GetOutputPort())
voxelActor = vtk.vtkActor()
voxelActor.SetMapper(voxelMapper)
Renderer.AddActor(voxelActor)
I have never used vtkImageReslice, but I have used vtkExtractVOI for vtkImageData, which allows you to achieve a similar result, I think. Here is your example modified with the latter, instead:
ImageOrigin = VoxelModel.GetOrigin()
slicer = vtk.vtkExtractVOI()
slicer.SetInputData(VoxelModel)
#With the setVOI method you can define which slice you want to extract
slicer.SetVOI(xmin, xmax, ymin, ymax, zslice, zslice)
slicer.SetSampleRate(1, 1, 1)
slicer.Update()
voxelSurface = vtk.vtkContourFilter()
voxelSurface.SetInputConnection(slicer.GetOutputPort())
voxelSurface.SetValue(0, .999)
voxelMapper = vtk.vtkPolyDataMapper()
voxelMapper.SetInputConnection(voxelSurface.GetOutputPort())
voxelActor = vtk.vtkActor()
voxelActor.SetMapper(voxelMapper)
Renderer.AddActor(voxelActor)

FLOT: How to make different colored points in same data series, connected by a line?

I think I may have stumbled onto a limitation of Flot, but I'm not sure. I'm trying to represent a single data series over time. The items' "State" is represented on the Y-Axis (there are 5 of them), and time is on the X-Axis (items can change states over time). I want the graph to have points and lines connecting those points for each data series.
In addition to tracking an item's State over time, I'd also like to represent it's "Status" at any of the particular points. This I would like to do by changing the color of the points. What this means is a single item may have different Statuses at different times, meaning for a single data series I need a line that connects different points (dots) of different colors.
The only thing I've seen so far is the ability to specify the color for all points in a given dataseries. Does anyone know if there's a way to specify colors individually?
Thanks.
There you go mate. You need to use a draw hook.
$(function () {
var d2 = [[0, 3], [4, 8], [8, 5], [9, 13]];
var colors = ["#cc4444", "#ff0000", "#0000ff", "#00ff00"];
var radius = [10, 20, 30, 40];
function raw(plot, ctx) {
var data = plot.getData();
var axes = plot.getAxes();
var offset = plot.getPlotOffset();
for (var i = 0; i < data.length; i++) {
var series = data[i];
for (var j = 0; j < series.data.length; j++) {
var color = colors[j];
var d = (series.data[j]);
var x = offset.left + axes.xaxis.p2c(d[0]);
var y = offset.top + axes.yaxis.p2c(d[1]);
var r = radius[j];
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(x,y,r,0,Math.PI*2,true);
ctx.closePath();
ctx.fillStyle = color;
ctx.fill();
}
}
};
var plot = $.plot(
$("#placeholder"),
[{ data: d2, points: { show: true } }],
{ hooks: { draw : [raw] } }
);
});
With 3 views, it may not be worth answering my own question, but here's the solution:
My original problem was how to plot a dataseries of points and a line, but with each point being a color that I specify.
Flot only allows specifying colors of the dots at the dataseries level, meaning each color must be its own dataseries. With this in mind, the solution is to make a single dataseries for each color, and draw that dataseries with only points, and no lines. Then I must make a separate dataseries that is all of the dots I want connected by the line, and draw that one with no points, and only a line.
So if I want to show a line going through 5 points with five different colors, I need 6 dataseries: 5 for each point, and 1 for the line that connects them. Flot will simply draw everything on top of each other, and I believe there's a way to specify what gets shown on top (to make sure the dots are shown above the line).
Actually, it's not very difficult to add a feature to flot that would call back into your code to get the color for each point. It took me about an hour, and I'm not a javascript expert by any measure.
If you look at drawSeriesPoints(), all you have to do is pass a callback parameter to plotPoints() which will be used to set ctx.strokeStyle. I added an option called series.points.colorCallback, and drawSeriesPoints() either uses that, or a simple function that always returns the series.color.
One tricky point: the index you should pass to your callback probably isn't the i in plotPoints(), but rather i/ps.
Hope this helps!

Resources