Been struggling for past week on how to place nodes on top of each other based on AABB.
So far with @xyz help i got the correct position of the top most extent of child nodes/meshes in node, but i cant figure out how to place the next node above it so that its bounding box starts where the existing bounding box ends.

Going from this:

To this:

THe node that is picked up and placed on top of other nodes could be in any orientation/rotation. And that node could have basepoint in the middle of mesh or at an end of a mesh. So the solution has to take it into account.

THe white plane is placed at the start of the node where all the other objects are placed and each next white plane is placed at the end of the existing extent of AABB. Which is correct, and then i want to place the picked up node on top of the plane, so that its AABB starts where the existing ones ends. Vector3(0,y,0)

func calculate_pickup_position(picked_up_obj:Item):

	var total_size:Vector3
	var p_aabb = picked_up_obj.item_mesh.global_transform* picked_up_obj.item_mesh.get_aabb()
	var p_aabb_o = picked_up_obj.item_mesh.get_aabb()
	var p_aabb_c = p_aabb.get_center()
	for n in pickup_node.get_children():
		if n is Item:
			var child:Item = n
			var mesh=child.item_mesh
			var m_aabb = mesh.global_transform*mesh.get_aabb().abs()

			total_size.y =  max(total_size.y,m_aabb.end.y) - pickup_node.global_position.y	
	
	var pp = PLANE.instantiate()
	
	if pickup_node.get_children().size() == 0:
		pickup_node.add_child(pp)
	else:
		pickup_node.add_child(pp)
		pp.position=total_size

	p_aabb.position.y=total_size.y
	total_size.y = p_aabb.get_center().y 
	
	return total_size

THe return value should be the :Item nodes position taking into account its own AABB and positioned i such way that its own AABB starts at the existing nodes AABB end (vertically, along Y axis)

The problem atm is that if the Item node is rotated so that its basepoint is not aligned with its own AABB position (starting corner ) then it is no placed correctly.

My latest attempt was to put the total_size.y value into the picked_up_obj aabb.position, and grab the new center of it, but now i need to somehow offset it from nodes.position. it works if the center position of AABB alighns with the nodes basepoint, but doesnt when basepoint is in different location ( you can see that hats basepoint is at the bottom of the AABB but the turned over potion bottle - 2nd potion, basepoint is about aligned with AABB center. Im lacking brains to do so 🙁... cant wrap my head around ..

  • xyz replied to this.

    kuligs2 Is each object you stack a single node, or some scene with a bunch of children as you've had in your previous related question?

    Btw you can't make meshes touch in all situations using only axis aligned boxes, as they are, well, always axis aligned. Consider using Godot's colliders if you need more realistic stacking.

      xyz at the moment item node contains pre defined properties that link to a visual mesh scene .item_mesh which is a meshinstance3d.

      I get what you mean, using colliders, but first i need to fugure out how to translate the rotated node and calculate the correct insertion point in to the parent node so that its AABB is placed above the existing AABB of all other child nodes.

      This is just an exercise using transforms, and real life situation on how to do things. It doesnt have to be realistic.

      • xyz replied to this.

        kuligs2 Calculate the global max y of the bounding box that's on top of the stack as we already determined in your previous question. In the same fashion you can calculate the global min y of the bounding box you're trying to place. The last thing you need is the global position if the node you're placing. To get the needed offset from the stack top just subtract those two things (aabb global min y and node's global y position)

        The most important thing is to keep all calculations in the same coordinate space. The global space is the best option here because it's easy to convert from/to, and simplest to mentally visualize.

          xyz Well thats where im having trouble. I got the top most Y (total_size.y) with the existing meshes in the parent node. then add the picked up nodes AABB with size (total_size.y = total_size.y + p_aabb.size.y). But somehow i cant get the offset.

          When i calculate var result_y = total_size.y - picked_up_obj.item_mesh.global_transform.origin.y it gives me the wrong offest.

          So far i got this

          	var total_size:Vector3
          	var p_aabb = picked_up_obj.item_mesh.global_transform * picked_up_obj.item_mesh.get_aabb()
          	for n in hand_node.get_children():
          		if n is Item:
          			var child:Item = n
          			var mesh=child.item_mesh
          			var m_aabb = mesh.global_transform*mesh.get_aabb().abs()
          
          			total_size.y =  max(total_size.y,m_aabb.end.y) - hand_node.global_position.y
          			
          	var pp = PLANE.instantiate()
          	
          	
          	if hand_node.get_children().size() == 0:
          		hand_node.add_child(pp)
          	else:
          		hand_node.add_child(pp)
          		pp.position=total_size
          
          	total_size.y = total_size.y + p_aabb.size.y
          	var result_y = total_size.y - picked_up_obj.item_mesh.global_transform.origin.y
          	total_size.y = result_y
          • xyz replied to this.

            xyz
            player.gd
            After calculations:

            picked_up_obj.pick_up(hand_node,result_y)

            Item.gd:

            func pick_up(player,pos):
            	
            	if picked_up_by == player:
            		
            		return
            		
            	pick_up_position = pos
            	
            	picked_up_by = player
            	original_transform = global_transform
            	
            	freeze = true
            	collision_layer = 0
            	collision_mask=0
            	
            	self.reparent(player)
            	pass
            
            func _physics_process(_delta):
            	if !picked_up_by: return
            	if is_in_position:return
            
            	if not global_transform.origin.distance_to(picked_up_by.global_transform.origin + pick_up_position) < 0.01:
            
            		global_transform.origin = lerp(global_transform.origin, picked_up_by.global_transform.origin + pick_up_position, speed*_delta)
            	else:
            		if not is_in_position:
            			is_in_position = true
            			print("Item in position!")
            	pass
            • xyz replied to this.

              kuligs2 If you're setting the position of picked_up_object then you should take its (global) position when calculating the offset, not the position of its child picked_up_object.item_mesh. Wehatever node you set the position of, its (global) position should be used to calculate the offset. Otherwise there might be a mismatch, as you're using one node to calculate the offset and then apply that offset to a different node.

                xyz doesnt this return AABB in global coordinates?

                var p_aabb = picked_up_obj.item_mesh.global_transform * picked_up_obj.item_mesh.get_aabb()

                And this:

                	for n in hand_node.get_children():
                		if n is Item:
                			var child:Item = n
                			var mesh=child.item_mesh
                			var m_aabb = mesh.global_transform*mesh.get_aabb().abs()
                
                			total_size.y =  max(total_size.y,m_aabb.end.y) - hand_node.global_position.y

                WHere

                var m_aabb = mesh.global_transform*mesh.get_aabb().abs()

                Is in global coords?

                then i just add picked up AABB size to the hand's AABB max Y value

                total_size.y = total_size.y + p_aabb.size.y

                And then calculate the picked_up_obj nodes offset:

                var result_y = total_size.y - picked_up_obj.item_mesh.global_transform.origin.y

                It seems to me that all these are in global coords?

                I dont see where im missing something?

                • xyz replied to this.

                  xyz

                  its the picked_up_obj's mesh instance

                  @onready var item_mesh:MeshInstance3D=get_mesh_instance(item_visual_node)
                  func get_mesh_instance(visual_node):
                  	
                  	for child in visual_node.get_children():
                  		if child is MeshInstance3D:
                  			return child
                  			
                  	return null
                  	
                  	
                  	pass

                  Its just a mesh of the object. Object has other properties as its part of the class Item

                  • xyz replied to this.

                    kuligs2 Well you take the position of this item_mesh node while calculating the offset, but when positioning your object, you don't position that node but instead you position the scene's top node. You need to use the top node for both. I.e. you need to calculate the offset for the same node you plan to reposition.

                    As I already said, make a simpler, minimal setup, preferably in a fresh project, and use it to test and figure things out. It'll be much easier to get the hang of things when there is no other stuff around. Also, when you have a minimal project that reproduces the problem, it's easy to share it so someone else can inspect it and see what exactly you're doing wrong.

                      xyz Setting up blank scene is troublesome, as real scene tend to be complicated and so in the end ill have to debug the complex scene.

                      Also i am lazy 😃

                      BTW. Seems like i finally got somewhat the correct positions.

                      Seems a bit off with this one, as to me it seem sto be floating too much but its most definately (aproximately) in position.

                      so the finisher move was this:
                      This part stays the same:

                      	var total_size:Vector3
                      	var p_aabb = picked_up_obj.item_mesh.global_transform * picked_up_obj.item_mesh.get_aabb()
                      	for n in hand_node.get_children():
                      		if n is Item:
                      			var child:Item = n
                      			var mesh=child.item_mesh
                      			var m_aabb = mesh.global_transform*mesh.get_aabb().abs()
                      
                      			total_size.y =  max(total_size.y,m_aabb.end.y) - hand_node.global_position.y

                      The c bellow is the offset thing that works for some reason. and then i just add the offset to the total_size which is the top_most_y of all child nodes in the hand_node.

                      	var c = picked_up_obj.global_transform.origin-p_aabb.get_center()+(p_aabb.size/2)
                      	var gg = total_size + c
                      	
                      	total_size.y = gg.y
                      	
                      
                      	return total_size

                      Works if hat is rotated a bit too

                      Now need to pull in them meshes closer with collision meshes.

                      One way would be lerp top mesh Y to negative value until the top mesh collides with bottom mesh.

                      • xyz replied to this.

                        kuligs2 One way would be lerp top mesh Y to negative value until the top mesh collides with bottom mesh.

                        Better to use binary search instead of lerping.

                          kuligs2 Oh, it totally applies. Lerping would be analogous to linear search so sampling collisions in binary search fashion may bring the number of iterations way down to logarithm of iteration you'd use with lerping. And since we're dealing with a continuum here, you'd get much more precision at the cost of just a few iterations as binary search converges very fast to the solution.

                            xyz Interesting, but does godot have some buit in methods/functions for this?
                            Oh the Array class seem to have it?


                            https://docs.godotengine.org/en/stable/classes/class_array.html#class-array-method-bsearch
                            But they dont call it binary, so im not 100% sure if it is binary search.

                            But need to wrap my head around on how to use this in my situation. Which values to iterate through.

                            The way i imagined it, i could just move some increment each _physics_process(_delta) frame until something collides, then stop.

                            Another idea was to somehow enable gravity for the top most object in the "hand" node and let it fall until it collides. But that gravity is like gravity for that top most node but its attached to the hand, so no matter where the hand is, and what orientation it is (upside down or whatever) the "moving" node will always move like it gravitates to the nearest collider object.

                            Will do some tests later to see how to do this, and see what works or not 🙂

                              kuligs2 If you don't need it to happen instantly then incrementing the position gradually in _physics_process() might be fine, you'll end up with some type of falling animation. Otherwise, if it has to happen instantly then a binary search would be better.

                              Note that you'd not use the literal binary search over discrete values, but instead you search for a non-colliding position that is very near to a colliding position, and you do this in a binary search manner: Start with two faraway positions, one that is not colliding and other that you're sure is colliding and then close in to the wanted position halving the increment to one side or other, just like binary search does with sorted values.

                              Alternatively if you want to use Array's binary search facilities, put all positions between those two extremes into an array (with some small increment), and then use Array::bsearch_custom() to do the search. This might actually be slower than using a custom iteration because you need to linearly iterate over all values when populating the array, but still faster than linear search because it doesn't require a collision check for each position in the array.

                              kuligs2 But they dont call it binary

                              A binary search means that what's being searched is iteratively divided by two. At each step you're halving what needs to be searched. I.e., binary = two.