Flower of Circles - continuation of circle

Godot Version

4.6 Stable

Question

So I had a go a thinking how to create this “flower of circles” , there could be probably a couple of doing but I try continue with loops and adding vertex .

Previously @normalized basically shown me how to do a very efficiently the loop, but as I trying to do a next loop based around a every 2nd point out of 3 ( so it could be shown with triangles ) I stuck with how to do loops in loops to makes it work .

I tried to check points but it clearly doesn’t rotate as I thought around new centre , should I add position_of_next_circle and some offset to determine a new point for rotation ?

for visualization the current result I would expect its something like this :

extends MeshInstance3D

func _ready():
	var arrays := []
	arrays.resize(Mesh.ARRAY_MAX)
	arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array()
	arrays[Mesh.ARRAY_COLOR] = PackedColorArray()
	
	var steps := 6
	var angle_step := TAU / steps
	
	# first circle
	for i in steps:
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.ZERO)
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.LEFT.rotated(Vector3.UP, angle_step * i))
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.LEFT.rotated(Vector3.UP, angle_step * (i + 1)))
		if i%3 == 0:
			var color := Color.WHITE
			arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
		elif i%2 == 0: 
			var color := Color.from_hsv(randf(), 0.8, randf())
			arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
			arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
		elif  i%1 == 0 and i%3 != 0:
			pass
			
	# points for next circles
	var vertex_size = int(arrays[Mesh.ARRAY_VERTEX].size())
	for n in vertex_size:
		if n%3 == 1:
			var position_of_next_circle = arrays[Mesh.ARRAY_VERTEX].get(n)
			for i in steps:
				arrays[Mesh.ARRAY_VERTEX].push_back(Vector3(position_of_next_circle))
				arrays[Mesh.ARRAY_VERTEX].push_back(Vector3(position_of_next_circle).rotated(Vector3.UP, angle_step * i))
				arrays[Mesh.ARRAY_VERTEX].push_back(Vector3(position_of_next_circle).rotated(Vector3.UP, angle_step * (i + 1)))
				var color := Color.RED
				arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
				print("n:", n, "i:", i,steps)
			print(position_of_next_circle)
		else:
			pass
			print(n)
			
			
	#add it into points
	print(arrays[Mesh.ARRAY_VERTEX])
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)

log

Godot Engine v4.6.stable.custom_build - https://godotengine.org
Metal 4.0 - Forward+ - Using Device #0: Apple - Apple M2 Max (Apple8)

0
n:1i:06
n:1i:16
n:1i:26
n:1i:36
n:1i:46
n:1i:56
(-1.0, 0.0, 0.0)
2
3
n:4i:06
n:4i:16
n:4i:26
n:4i:36
n:4i:46
n:4i:56
(-0.5, 0.0, 0.866025)
5
6
n:7i:06
n:7i:16
n:7i:26
n:7i:36
n:7i:46
n:7i:56
(0.5, 0.0, 0.866025)
8
9
n:10i:06
n:10i:16
n:10i:26
n:10i:36
n:10i:46
n:10i:56
(1.0, 0.0, -0.0)
11
12
n:13i:06
n:13i:16
n:13i:26
n:13i:36
n:13i:46
n:13i:56
(0.5, 0.0, -0.866025)
14
15
n:16i:06
n:16i:16
n:16i:26
n:16i:36
n:16i:46
n:16i:56
(-0.5, 0.0, -0.866025)
17
[(0.0, 0.0, 0.0), (-1.0, 0.0, 0.0), (-0.5, 0.0, 0.866025), (0.0, 0.0, 0.0), (-0.5, 0.0, 0.866025), (0.5, 0.0, 0.866025), (0.0, 0.0, 0.0), (0.5, 0.0, 0.866025), (1.0, 0.0, -0.0), (0.0, 0.0, 0.0), (1.0, 0.0, -0.0), (0.5, 0.0, -0.866025), (0.0, 0.0, 0.0), (0.5, 0.0, -0.866025), (-0.5, 0.0, -0.866025), (0.0, 0.0, 0.0), (-0.5, 0.0, -0.866025), (-1.0, 0.0, 0.0), (-1.0, 0.0, 0.0), (-1.0, 0.0, 0.0), (-0.5, 0.0, 0.866025), (-1.0, 0.0, 0.0), (-0.5, 0.0, 0.866025), (0.5, 0.0, 0.866025), (-1.0, 0.0, 0.0), (0.5, 0.0, 0.866025), (1.0, 0.0, -0.0), (-1.0, 0.0, 0.0), (1.0, 0.0, -0.0), (0.5, 0.0, -0.866025), (-1.0, 0.0, 0.0), (0.5, 0.0, -0.866025), (-0.5, 0.0, -0.866025), (-1.0, 0.0, 0.0), (-0.5, 0.0, -0.866025), (-1.0, 0.0, 0.0), (-0.5, 0.0, 0.866025), (-0.5, 0.0, 0.866025), (0.5, 0.0, 0.866025), (-0.5, 0.0, 0.866025), (0.5, 0.0, 0.866025), (1.0, 0.0, -0.0), (-0.5, 0.0, 0.866025), (1.0, 0.0, -0.0), (0.5, 0.0, -0.866026), (-0.5, 0.0, 0.866025), (0.5, 0.0, -0.866026), (-0.5, 0.0, -0.866025), (-0.5, 0.0, 0.866025), (-0.5, 0.0, -0.866025), (-1.0, 0.0, -0.0), (-0.5, 0.0, 0.866025), (-1.0, 0.0, -0.0), (-0.5, 0.0, 0.866026), (0.5, 0.0, 0.866025), (0.5, 0.0, 0.866025), (1.0, 0.0, -0.0), (0.5, 0.0, 0.866025), (1.0, 0.0, -0.0), (0.5, 0.0, -0.866026), (0.5, 0.0, 0.866025), (0.5, 0.0, -0.866026), (-0.5, 0.0, -0.866025), (0.5, 0.0, 0.866025), (-0.5, 0.0, -0.866025), (-1.0, 0.0, 0.0), (0.5, 0.0, 0.866025), (-1.0, 0.0, 0.0), (-0.5, 0.0, 0.866025), (0.5, 0.0, 0.866025), (-0.5, 0.0, 0.866025), (0.5, 0.0, 0.866025), (1.0, 0.0, -0.0), (1.0, 0.0, -0.0), (0.5, 0.0, -0.866026), (1.0, 0.0, -0.0), (0.5, 0.0, -0.866026), (-0.5, 0.0, -0.866025), (1.0, 0.0, -0.0), (-0.5, 0.0, -0.866025), (-1.0, 0.0, 0.0), (1.0, 0.0, -0.0), (-1.0, 0.0, 0.0), (-0.5, 0.0, 0.866026), (1.0, 0.0, -0.0), (-0.5, 0.0, 0.866026), (0.5, 0.0, 0.866025), (1.0, 0.0, -0.0), (0.5, 0.0, 0.866025), (1.0, 0.0, -0.0), (0.5, 0.0, -0.866025), (0.5, 0.0, -0.866025), (-0.5, 0.0, -0.866025), (0.5, 0.0, -0.866025), (-0.5, 0.0, -0.866025), (-1.0, 0.0, 0.0), (0.5, 0.0, -0.866025), (-1.0, 0.0, 0.0), (-0.5, 0.0, 0.866026), (0.5, 0.0, -0.866025), (-0.5, 0.0, 0.866026), (0.5, 0.0, 0.866025), (0.5, 0.0, -0.866025), (0.5, 0.0, 0.866025), (1.0, 0.0, 0.0), (0.5, 0.0, -0.866025), (1.0, 0.0, 0.0), (0.5, 0.0, -0.866026), (-0.5, 0.0, -0.866025), (-0.5, 0.0, -0.866025), (-1.0, 0.0, -0.0), (-0.5, 0.0, -0.866025), (-1.0, 0.0, -0.0), (-0.5, 0.0, 0.866025), (-0.5, 0.0, -0.866025), (-0.5, 0.0, 0.866025), (0.5, 0.0, 0.866025), (-0.5, 0.0, -0.866025), (0.5, 0.0, 0.866025), (1.0, 0.0, 0.0), (-0.5, 0.0, -0.866025), (1.0, 0.0, 0.0), (0.5, 0.0, -0.866025), (-0.5, 0.0, -0.866025), (0.5, 0.0, -0.866025), (-0.5, 0.0, -0.866025)]

Demonstation in video format , points, triangles, lines:


started implementing some manual grid to better understand some of the things .

Currently I needed rotate 90 degrees on X for MeshInstance3D with Mesh : TextMesh to match rotation of camera from below ( -2.78 on Y , and Rotation 90 degree)

Why do I rotate camera ?
as for simple code like this for grid ( I know can do loops, but wanted to make it obvious as only can )

extends MeshInstance3D

func _ready() -> void:
	var grid_array := []
	grid_array.resize(Mesh.ARRAY_MAX)
	grid_array[Mesh.ARRAY_VERTEX] = PackedVector3Array()
	grid_array[Mesh.ARRAY_COLOR] = PackedColorArray()
	# horizontal cross middle
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,0))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,0))
	grid_array[Mesh.ARRAY_COLOR].append(Color.BLUE)
	grid_array[Mesh.ARRAY_COLOR].append(Color.BLUE)
	#vertical cross middle
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.BLUE)
	grid_array[Mesh.ARRAY_COLOR].append(Color.BLUE)
	#vetical grid +4 , 0.2 increment
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0.8,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0.8,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0.6,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0.6,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0.4,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0.4,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0.2,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(0.2,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	#vetical grid -4, 0.2 increment
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-0.8,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-0.8,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-0.6,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-0.6,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-0.4,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-0.4,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-0.2,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-0.2,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	grid_array[Mesh.ARRAY_COLOR].append(Color.WEB_GREEN)
	#horizontal grid +4, 0.2 increment
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,0.8))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,0.8))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,0.6))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,0.6))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,0.4))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,0.4))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,0.2))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,0.2))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	#horizontal grid -4, 0.2 increment
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,-1))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,-1))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,-0.8))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,-0.8))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,-0.6))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,-0.6))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,-0.4))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,-0.4))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(1.0,0,-0.2))
	grid_array[Mesh.ARRAY_VERTEX].append(Vector3(-1.0,0,-0.2))
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	grid_array[Mesh.ARRAY_COLOR].append(Color.DARK_RED)
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINES, grid_array)
	var size_of_grid = (grid_array[Mesh.ARRAY_VERTEX].size())
	for i in size_of_grid:
		print("index number: ", i,grid_array[Mesh.ARRAY_VERTEX].get(i))
		

you see it rendered from below , with camera 0 degree rotation and 0 offset on axis this is what is visible

ok this can be easily changed for more like game camera from 3rd person :slight_smile:



resetting text_mesh to 0 degree .
but problem comes when from lines I switch back to triangle

	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)

ok this can be fixed with

		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.LEFT.rotated(Vector3.DOWN, angle_step * i))
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.LEFT.rotated(Vector3.DOWN, angle_step * (i + 1)))

but does it actually match position of global ?

extends MeshInstance3D

func _ready():
	var arrays := []
	arrays.resize(Mesh.ARRAY_MAX)
	arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array()
	arrays[Mesh.ARRAY_COLOR] = PackedColorArray()
	
	var steps := 6
	var angle_step := TAU / steps
	
	# first circle
	for i in steps:
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.ZERO)
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.LEFT.rotated(Vector3.DOWN, angle_step * i))
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.LEFT.rotated(Vector3.DOWN, angle_step * (i + 1)))
		if i%3 == 0:
			var color := Color.BLUE_VIOLET
			arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
		elif i%2 == 0: 
			var color := Color.DARK_MAGENTA
			arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
			arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
		elif  i%1 == 0 and i%3 != 0:
			pass
			
	#add it into points
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	var size_of_vertex_array = arrays[Mesh.ARRAY_VERTEX].size()
	for x in size_of_vertex_array:
		print("index number: ",x,arrays[Mesh.ARRAY_VERTEX].get(x))

index number: 0(0.0, 0.0, 0.0)
index number: 1(-1.0, 0.0, 0.0)
index number: 2(-0.5, 0.0, -0.866025)
index number: 3(0.0, 0.0, 0.0)
index number: 4(-0.5, 0.0, -0.866025)
index number: 5(0.5, 0.0, -0.866025)
index number: 6(0.0, 0.0, 0.0)
index number: 7(0.5, 0.0, -0.866025)
index number: 8(1.0, 0.0, 0.0)
index number: 9(0.0, 0.0, 0.0)
index number: 10(1.0, 0.0, 0.0)
index number: 11(0.5, 0.0, 0.866025)
index number: 12(0.0, 0.0, 0.0)
index number: 13(0.5, 0.0, 0.866025)
index number: 14(-0.5, 0.0, 0.866025)
index number: 15(0.0, 0.0, 0.0)
index number: 16(-0.5, 0.0, 0.866025)
index number: 17(-1.0, 0.0, -0.0)

so if change it to clockwise it should use VECTOR3.RIGHT

		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.RIGHT.rotated(Vector3.DOWN, angle_step * i))
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.RIGHT.rotated(Vector3.DOWN, angle_step * (i + 1)))

now this makes a bit more sense , since this is 3 vertices for each triangle , it will overlaps . clearly 0 , 3 are same ( as centre ) .

What will I need for outside circles ?
index 1 or 2 ?

	for x in size_of_vertex_array:
		print("index number: ",x,arrays[Mesh.ARRAY_VERTEX].get(x))
		if x%3 == 1:
			print("vertex centre for next circle : ","index number: ",x,arrays[Mesh.ARRAY_VERTEX].get(x) )

index number: 0(0.0, 0.0, 0.0)
index number: 1(1.0, 0.0, 0.0)
vertex centre for next circle : index number: 1(1.0, 0.0, 0.0)
index number: 2(0.5, 0.0, 0.866025)
index number: 3(0.0, 0.0, 0.0)
index number: 4(0.5, 0.0, 0.866025)
vertex centre for next circle : index number: 4(0.5, 0.0, 0.866025)
index number: 5(-0.5, 0.0, 0.866025)
index number: 6(0.0, 0.0, 0.0)
index number: 7(-0.5, 0.0, 0.866025)
vertex centre for next circle : index number: 7(-0.5, 0.0, 0.866025)
index number: 8(-1.0, 0.0, -0.0)
index number: 9(0.0, 0.0, 0.0)
index number: 10(-1.0, 0.0, -0.0)
vertex centre for next circle : index number: 10(-1.0, 0.0, -0.0)
index number: 11(-0.5, 0.0, -0.866025)
index number: 12(0.0, 0.0, 0.0)
index number: 13(-0.5, 0.0, -0.866025)
vertex centre for next circle : index number: 13(-0.5, 0.0, -0.866025)
index number: 14(0.5, 0.0, -0.866025)
index number: 15(0.0, 0.0, 0.0)
index number: 16(0.5, 0.0, -0.866025)
vertex centre for next circle : index number: 16(0.5, 0.0, -0.866025)
index number: 17(1.0, 0.0, 0.0)

is this correct approach or do I miss something ?

Keep trying do a offset and multiplier but it seem to be limiting the fact .rotated() only accept normalized

extends MeshInstance3D

func _ready():
	var arrays := []
	arrays.resize(Mesh.ARRAY_MAX)
	arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array()
	arrays[Mesh.ARRAY_COLOR] = PackedColorArray()
	
	var steps := 6
	var steps_secondary_circles := 12
	var angle_step := TAU / steps
	var angle_steps_secondary_circles := TAU / steps_secondary_circles
	
	# first circle
	for i in steps:
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.ZERO)
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.RIGHT.rotated(Vector3.DOWN, angle_step * i))
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.RIGHT.rotated(Vector3.DOWN, angle_step * (i + 1)))
		if i%3 == 0:
			var color := Color.BLUE_VIOLET
			arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
		elif i%2 == 0: 
			var color := Color.DARK_MAGENTA
			arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
			arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
		elif  i%1 == 0 and i%3 != 0:
			pass
			
	#add it into points
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	var size_of_vertex_array = arrays[Mesh.ARRAY_VERTEX].size()
	for x in size_of_vertex_array:
		var new_vertex = arrays[Mesh.ARRAY_VERTEX].get(x)
		if x%3 == 1:
			for y in steps_secondary_circles:
				if x == 1:
					arrays[Mesh.ARRAY_VERTEX].push_back((Vector3(new_vertex)))
					arrays[Mesh.ARRAY_VERTEX].push_back((Vector3(new_vertex).rotated(Vector3.DOWN, angle_steps_secondary_circles * y)))
					arrays[Mesh.ARRAY_VERTEX].push_back((Vector3(new_vertex).rotated(Vector3.DOWN, angle_steps_secondary_circles * (y + 1))))
					var color := Color.DARK_ORANGE
					arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
					print("color, circle x :" ,x, " y : ", y )
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINES, arrays)

so I think calculating offset and adding could be the most sensible thing to do

extends MeshInstance3D

func _ready():
	var arrays := []
	arrays.resize(Mesh.ARRAY_MAX)
	arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array()
	arrays[Mesh.ARRAY_COLOR] = PackedColorArray()
	
	var steps := 32
	var angle_step := TAU / steps
	
	# first circle
	for i in steps:
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.ZERO)
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.RIGHT.rotated(Vector3.DOWN, angle_step * i))
		var color := Color.WHITE
		arrays[Mesh.ARRAY_COLOR].append_array([color, color])
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINES, arrays)
	#add it into points
	var size_of_vertex_array = arrays[Mesh.ARRAY_VERTEX].size()
	var size_of_color_array = arrays[Mesh.ARRAY_COLOR].size()
	print("vertex array size:",size_of_vertex_array,"color array size",size_of_color_array)
	for x in size_of_vertex_array:
		var offset = arrays[Mesh.ARRAY_VERTEX].get(x)
		if x%3 == 1:
			for n in size_of_vertex_array:
				var new_vertex = arrays[Mesh.ARRAY_VERTEX].get(n) + offset
				var new_color = Color.BLUE
				arrays[Mesh.ARRAY_VERTEX].push_back(new_vertex)
				if n%2 ==0:
					new_color = Color.RED
				if n%3 ==0:
					new_color = Color.GREEN
				arrays[Mesh.ARRAY_COLOR].push_back(new_color)
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINES, arrays)
	size_of_vertex_array = arrays[Mesh.ARRAY_VERTEX].size()
	size_of_color_array = arrays[Mesh.ARRAY_COLOR].size()
	print("vertex array size:",size_of_vertex_array,"color array size",size_of_color_array)

There’s a neat thing about packed vector arrays; they have overloaded multiplication operator with transforms. You can transform the whole array by a single multiplication operation.

So make a vertex array for a single circle and then just array-append its transformed variants to the main vertex array.

2 Likes

this sounds really easy, but I have no idea how to convert Vector3 into transform.

Should I setup some temporary variable or how to append it ?

	var size_array = arrays[Mesh.ARRAY_VERTEX].size()
	for n in size_array:
		if n%3 ==1:
			var offset = arrays[Mesh.ARRAY_VERTEX].get(n)

Why would you need to convert a vector into a transform.

If you want to make a transform that translates along a vector then: Transform3D().translated(Vector3(dx, dy, dz))

1 Like

for multiplication I see only that option there using Transform3D

Yes. What else would you need? Transform3D can do anything; translate, rotate, scale, skew, and any combination of those.

1 Like

Keep trying a different things but can’t crack how to use only those 6 independently size of steps for Transform3d Transform

extends MeshInstance3D

func _ready():
	var arrays := []
	arrays.resize(Mesh.ARRAY_MAX)
	arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array()
	arrays[Mesh.ARRAY_COLOR] = PackedColorArray()
	var steps := 12
	var angle_step := TAU / steps
	
	# first circle
	for i in steps:
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.ZERO)
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.RIGHT.rotated(Vector3.DOWN, angle_step * i))
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.RIGHT.rotated(Vector3.DOWN, angle_step * (i+1)))
		var color := Color.WHITE
		arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	
	var size_array = arrays[Mesh.ARRAY_VERTEX].size()

	for n in size_array:
		if n%6==1:
			print(n)
			var offset =arrays[Mesh.ARRAY_VERTEX].get(n)
			arrays[Mesh.ARRAY_VERTEX].append_array(arrays[Mesh.ARRAY_VERTEX] * Transform3D().translated(Vector3(offset)))
			arrays[Mesh.ARRAY_COLOR].append_array(arrays[Mesh.ARRAY_COLOR])
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	size_array = arrays[Mesh.ARRAY_VERTEX].size()
	print(size_array)
		

for 12 steps this works , but size of array seem to be wrong

1
7
13
19
25
31
2304

increase to 32

1
7
13
19
25
31
37
43
49
55
61
67
73
79
85
91
6291456

tried a bit easier approach but results here a bit off

extends MeshInstance3D

func _ready():
	var arrays := []
	arrays.resize(Mesh.ARRAY_MAX)
	arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array()
	arrays[Mesh.ARRAY_COLOR] = PackedColorArray()
	var steps := 64
	var angle_step := TAU / steps
	for i in steps:
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.ZERO)
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.RIGHT.rotated(Vector3.DOWN, angle_step * i))
		arrays[Mesh.ARRAY_VERTEX].push_back(Vector3.RIGHT.rotated(Vector3.DOWN, angle_step * (i+1)))
		var color := Color.WHITE
		arrays[Mesh.ARRAY_COLOR].append_array([color, color, color])
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	
	var size_array = arrays[Mesh.ARRAY_VERTEX].size()
	var sixth_of_array = size_array / 6
	for n in size_array:
		if n%sixth_of_array==1:
			var offset =arrays[Mesh.ARRAY_VERTEX].get(n)
			arrays[Mesh.ARRAY_VERTEX].append_array(arrays[Mesh.ARRAY_VERTEX] * Transform3D().translated(Vector3(offset)))
			arrays[Mesh.ARRAY_COLOR].append_array(arrays[Mesh.ARRAY_COLOR])
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	size_array = arrays[Mesh.ARRAY_VERTEX].size()
		

extends MeshInstance3D

func _ready():
	var arrays := []
	arrays.resize(Mesh.ARRAY_MAX)
	arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array()
	
	var circle_steps := 64
	var circle := PackedVector3Array()
	for i in circle_steps:
		circle.push_back(Vector3.RIGHT.rotated(Vector3.UP, i * TAU / circle_steps))
		circle.push_back(Vector3.RIGHT.rotated(Vector3.UP, (i + 1) * TAU / circle_steps))
	
	arrays[Mesh.ARRAY_VERTEX].append_array(circle)
	var circle_count = 6
	for i in circle_count:
		var transform := Transform3D().translated(Vector3.RIGHT).rotated(Vector3.UP, i * TAU / circle_count)
		arrays[Mesh.ARRAY_VERTEX].append_array(transform * circle)
	
	mesh = ArrayMesh.new()
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINES, arrays)

You can play with parameters. For example, change circle_count and circle_steps to 12 and you get:

1 Like

WoW this is amazing, thank you so much , I was struggling to find solution a lot :slight_smile:

Also still not entirely understand if I want for each new circle make a new color what be a best method with PackColorArray as it only takes + not multiplier

extends MeshInstance3D

var t := 0.0

func _process(dt):
	t += dt
	var arrays := []
	arrays.resize(Mesh.ARRAY_MAX)
	arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array()
	arrays[Mesh.ARRAY_COLOR] = PackedColorArray()

	var circle_steps := 64
	var circle := PackedVector3Array()
	for i in circle_steps:
		circle.push_back(Vector3.RIGHT.rotated(Vector3.UP, i * TAU / circle_steps))
		circle.push_back(Vector3.RIGHT.rotated(Vector3.UP, (i + 1) * TAU / circle_steps))

	var color := PackedColorArray()
	color.resize(circle.size())
	color.fill(-1)
	
	arrays[Mesh.ARRAY_VERTEX].append_array(circle)
	arrays[Mesh.ARRAY_COLOR].append_array(color)
	var circle_count = 6
	for i in circle_count:
		var transform := Transform3D().translated(Vector3.RIGHT * abs(sin(t))).rotated(Vector3.UP, i * TAU / circle_count)
		arrays[Mesh.ARRAY_VERTEX].append_array(transform * circle)
		color.fill(Color.from_hsv(float(i) / circle_count, 1.0, 0.7))
		arrays[Mesh.ARRAY_COLOR].append_array(color)
	
	mesh = ArrayMesh.new()
	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINES, arrays)

circles

1 Like

nice , you have used transform to create animation :flexed_biceps:.

color.fill(-1)
what is this doing ?

ok got it , it change color of initial circle , interestingly it takes different format