box selecting 3d

Blitz3D Forums/Blitz3D Beginners Area/box selecting 3d

I am getting frusterated at this code. It is used when the mouse is released after dragging. It determines if a unit is inside the box or not.
	For unit.unit = Each unit
		CameraProject camera,EntityX#(unit\entity),EntityY#(unit\entity),EntityZ#(unit\entity)
		tempx# = ProjectedX#(): tempy# = Projectedy#()
		If ((tempx > x1) And (tempx < x2)) And ((tempy > y1) And (tempy < y1)) Then 
			unit\selected = True
		Else
			unit\selected = False	
		End If
	Next
End Function

where x1 and y1 is top left corner and x2,y2 is bottom right corner. Why does this code not work?

Hard to say. You don't tell us much information, like what kind of results you get.

All I can say at this point is;
Have you tried using the Global flag with your EntityXYZ# commands?
Have you verified that x1,x2,y1,y2 all have valid values (I can't tell from your code)?

You realise that in the code above you have used 'y1' twice.

It should read:

if ((tempx>x1) and (tempx<x2)) and ((tempy>y1) and (tempy<y2)) then ....


even with the change the program does not work. Here is an example illustrating what I am trying to do.

Graphics3D 800,600,0,2
SetBuffer BackBuffer()

Global gBoxSelecting, gBoxSelectX#,gBoxSelectY#
Global selected_entity = 0
Global selection = 0


Type unit
	Field entity,selected
End Type

cube.unit = New unit
cube\entity = CreateCube()
cube\selected = False
EntityPickMode cube\entity,2

Global camera = CreateCamera()
PositionEntity camera, 0,10,-10
CameraRange camera,0.9,1000
CameraClsColor camera,0,0,0
PointEntity camera,cube\entity

Global light = CreateLight()


Repeat
	
	userinput()
	RenderWorld()
	render2dstuff()
	Flip
	
Until KeyHit(1)
End


Function userinput()
	If MouseHit(1)
		For unit.unit = Each unit
			unit\selected = False
			
			selected_entity = Camera_Pick(camera,MouseX(),MouseY(),unit)
			If selection = 1 Then 
				unit\selected = True
			EndIf
			
			If selection = 0
				gBoxSelecting = True
				gBoxSelectX = MouseX()
				gBoxSelectY = MouseY()
			EndIf	
		Next
	;Box select once the mouse is released.	
	Else If MouseDown(1) = False And gBoxSelecting = True	
		BoxSelectUnits()
	EndIf	
End Function

Function render2dstuff()
		;draw selecting box
		If gBoxSelecting = True
			Color 0,255,0
			Line gBoxSelectX,gBoxSelectY,MouseX(),gBoxSelectY
			Line MouseX(),gBoxSelectY,MouseX(),MouseY()
			Line MouseX(),MouseY(),gBoxSelectX,MouseY()
			Line gBoxSelectX,MouseY(),gBoxSelectX,gBoxSelectY							
		End If
	Color 100,0,255	
	Text 0,0,"mouse down = "+MouseDown(1)
	Text 0,10,"entity selected = "+selected_entity+"  selection = "+selection
End Function


Function boxSelectUnits()
	gBoxSelecting = False
	x1 = Min(gBoxSelectX,MouseX()) : x2 = Max(gBoxSelectX,MouseX())	
	y1 = Min(gBoxSelectY,MouseY()) : y2 = Max(gBoxSelectY,MouseY())	
	
	For unit.unit = Each unit
		CameraProject camera,EntityX#(unit\entity),EntityY#(unit\entity),EntityZ#(unit\entity)
		tempx# = ProjectedX#() : tempy# = ProjectedY#()
		If ((tempx > x1) And (tempx < x2)) And ((tempy > y1) And (tempy < y2)) Then 
			unit\selected = True
		Else
			unit\selected = False	
		End If
	Next
End Function

Function Camera_Pick (camera,x,y,unit.unit)

	CameraPick(camera,x,y)
	If PickedEntity() <> 0 Then
	
		selected_entity = PickedEntity()
		selection = 1
		Return PickedEntity()
	
	Else
		selected_entity = 0
		selection = 0
		Return 0
	End If
	
	
	
End Function
;Returns the lesser of two numbers.
Function Min#(a#,b#)
	If a < b Then Return a
	Return b
End Function

;Returns the greater of two numbers.
Function Max#(a#,b#)
	If a > b Then Return a
	Return b
End Function


I get a MAV with that program. Do you?

If this is 3d, why not, grab the XYZ co-ords of a CameraPick, for the initial positions, then grab the XYZ co-ords of a CameraPick on releasing the mouse, and see if an of the entities centre points lie within this?

sorry blitznerd, forgot to put in graphics 3d. if you try it now it should work.

Ross, that is a great suggestion! I will try it out. In the meantime, why would the way I did it not work (for future reference)?

How exactly does camera pick work? I know that it goes the way the camera is facing at screen coordinates inputed in the function. If it encounters no entities,however, how do I get the coordinates of the camera pick? If no entity is picked then pickedx, pickedy, and pickedz all return nothing.

I'm not entirely sure why. I take it your projecting the entity's co-ords, into the screen co-ords, and then checking to see if the point lies within the box? (Come to think of it, that's prolly a better idea :o))

Try this:

Graphics3D 800,600
SetBuffer BackBuffer()


Global camera = CreateCamera()
PositionEntity camera,0,5,-10
RotateEntity camera,45,0,0

Global light = CreateLight()

Global plane = CreatePlane()
PositionEntity plane,0,-0.5,0
EntityColor plane,100,255,100
EntityPickMode plane,2

Global sx = 0
Global sy = 0

Global st_x# = 0
Global st_z# = 0
Global fin_x# = 0
Global fin_z# = 0
Global mouse_down = 0

Type entity
	Field mesh
	Field selected
End Type

e.entity = New entity
e\mesh = CreateCube()
EntityColor e\mesh,0,100,0
e\selected = 0
PositionEntity e\mesh,3,0,3

e.entity = New entity
e\mesh = CreateCube()
EntityColor e\mesh,0,100,0
e\selected = 0
PositionEntity e\mesh,3,0,-3



While Not KeyHit(1)


	If MouseDown(1) Then
		If mouse_down = 0 Then
			mouse_down = 1
			CameraPick(camera,MouseX(),MouseY())
			st_x = PickedX()
			st_z = PickedZ()
			DebugLog("setting - "+st_x+","+st_z)
			sx = MouseX()
			sy = MouseY()
		End If
	Else
		If mouse_down = 1 Then
			CameraPick(camera,MouseX(),MouseY())
			fin_x = PickedX()
			fin_z = PickedZ()
			If fin_x < st_x Then
				temp# = st_x
				st_x = fin_x
				fin_x = temp
			End If
			If fin_z < st_z Then
				temp# = st_z
				st_z = fin_z
				fin_z = temp
			End If
			mouse_down = 0
			select_entity()
		End If
	End If
	
	UpdateWorld
	RenderWorld
	draw_bounding_box()
	Rect MouseX(),MouseY(),2,2
	Text 0,0,st_x+" , "+st_z
	Flip
Wend
End

Function draw_bounding_box()

	If mouse_down = 1 Then
		temp_sx = sx
		temp_sy = sy
		temp_fx = MouseX()
		temp_fy = MouseY()
			If temp_fx < temp_sx Then
				temp# = temp_sx
				temp_sx = temp_fx
				temp_fx = temp
			End If
			If temp_fy < temp_sy Then
				temp# = temp_sy
				temp_sy = temp_fy
				temp_fy = temp
			End If
		Rect temp_sx,temp_sy,temp_fx-temp_sx,temp_fy-temp_sy,0
	End If

End Function

Function select_entity()

	For e.entity = Each entity
		If EntityX(e\mesh) > st_x And EntityX(e\mesh) < fin_x Then
			If EntityZ(e\mesh) > st_z And EntityZ(e\mesh) < fin_z Then
				e\selected = 1
				EntityColor e\mesh,0,255,0
				DebugLog("selected!")
			Else
				e\selected = 0
				EntityColor e\mesh,0,100,0
			End If
		Else
			e\selected = 0
			EntityColor e\mesh,0,100,0
		End If
	Next
	
End Function


Ok, i've looked at your code. You entity is being selected. If you debuglog() the unit\selected part:

		If tempx > x1 And tempx < x2 And tempy > y1 And tempy < y2 Then 
			unit\selected = True
			DebugLog("Selected")
		Else
			unit\selected = False	
		End If


It's just that your selected_entity variable doesn't get set. But, if your bandbox selecting, you could only hold one entity in that anyway. So, your selection method works ;o)

WHat to do, if your using a camera pick, is put a large, invisible quad/sprite (Entity alpha, NOT hide entity), to stop the pick reaching too far. Long picks are slow, so this will act as a stopper. If the pick returns nothing, it will be:

If PickedEntity() = 0 then
  ; no entity picked
end if


In my example, i use a plane to pick from. The camerapick collides with the plane, i grab the co-ords from that. Your way of doing this, is alot better, so i'd definetly stick with it :o)

Long picks are slow
Hmm, yeah - I wonder why CameraPick doesn't have an optional 'pick_distance' parameter?!

Mark!!! :P

It does, it's called CameraRange.

Er, OK, but that's not exactly what I meant. :)

Thanks for all the help! It really showed me a lot about camera project and entity pick.

Thanks!