Pick 3d objects in a rect !

Blitz3D Forums/Blitz3D Programming/Pick 3d objects in a rect !

GC-Martijn(Posted 2003) [#1]
I'm busy with making a strategie game and now i'm building a multiply select Rect.

if you run this code you see a cool begin.
That's oke but how to select all the object inside that rect?

Please edit the code below,
I try everything but nothing works,

Graphics3D 320,240,32,2
Global cursor

SetBuffer BackBuffer()

camera=CreateCamera() 
light=CreateLight() 
RotateEntity light,90,0,0 

;cursor
cursor	= LoadImage("cursor.png")
MaskImage cursor,0,255,255

;pickable object 1
cone=CreateCone(20) 
EntityPickMode cone,2
PositionEntity cone,0,0,5 

;pickable object 2
cone2=CreateCone(5) 
EntityPickMode cone2,2
PositionEntity cone2,3,3,3 

While Not KeyHit(1)
 
RenderWorld
UpdateWorld

mX	=MouseX()
mY	=MouseY()

If MouseHit(1)
selectX	= MouseX()
selectY	= MouseY()
EndIf
;---+
	If MouseDown(1)	
	m_width	= mX-selectX
	m_height= mY-selectY
	Rect selectX,selectY,m_width,m_height,0
	CameraPick(camera,mX,mY)
	EndIf

DrawImage cursor,mX,mY

If PickedEntity() Then
Text 0,0,"PickedEntity: "+PickedEntity() 
EndIf

Flip
Wend
End



Todd(Posted 2003) [#2]
Hi Martijn,

You have a pretty good start for doing that, but the only problem with it is that CameraPick() can only pick on entity at a time. To be able to select more than one entity you will need to have a variable for each object telling wheter it was picked or not, and read through and pick from each position inside the rectangle. The only way i can really explain this is with an example, so try this:

Graphics3D 640,480,0,2
SetBuffer BackBuffer()

Camera=CreateCamera()
PositionEntity Camera,0,0,-6

Cube1=CreateCube()
PositionEntity Cube1,-3,2,0
EntityColor Cube1,128,0,0
EntityPickMode Cube1,2

Cube2=CreateCube()
PositionEntity Cube2,0,0,0
EntityColor Cube2,0,128,0
EntityPickMode Cube2,2

Cube3=CreateCube()
PositionEntity Cube3,3,-2,0
EntityColor Cube3,0,0,128
EntityPickMode Cube3,2

Light=CreateLight()
AmbientLight 50,50,50
TurnEntity Light,30,50,0

Global Cube1Sel=0
Global Cube2Sel=0
Global Cube3Sel=0

While Not KeyDown(1)
Cls

	If Cube1Sel=1
		EntityColor Cube1,255,0,0
	Else
		EntityColor Cube1,128,0,0
	EndIf

	If Cube2Sel=1
		EntityColor Cube2,0,255,0
	Else
		EntityColor Cube2,0,128,0
	EndIf
	
	If Cube3Sel=1
		EntityColor Cube3,0,0,255
	Else
		EntityColor Cube3,0,0,128
	EndIf

UpdateWorld
RenderWorld

	If MouseDown(1)
		If mouseSet=0
			mouseSet=1
			mX=MouseX()
			mY=MouseY()
		EndIf
		Rect mX,mY,MouseX()-mX,MouseY()-mY,0
	Else
		If mouseSet=1
			mouseSet=0
			Cube1Sel=0
			Cube2Sel=0
			Cube3Sel=0
			For x=0 To MouseX()-mX Step 4
			For y=0 To MouseY()-mY Step 4
				Select CameraPick(Camera,mX+x,mY+y)
				Case Cube1
					Cube1Sel=1
				Case Cube2
					Cube2Sel=1
				Case Cube3	
					Cube3Sel=1
				End Select
			Next
			Next
		EndIf
	EndIf

Flip
Wend


If you don't quite understand all of this, just ask and I'll explain, otherwise I'll leave it up to you :) Hope that helps you out some.


GC-Martijn(Posted 2003) [#3]
Thanks Todd

I test your script and it works good :)
The only thing I saw that its very slow :( why is that?

When i'm at home i'm going to edit it.

Thank You Very Much
--


Todd(Posted 2003) [#4]
I'm not sure why it would be slow. Is it always slow, or just after you drag over an area. If it is that just try changing 'Step 4' in the code to something higher, like 'Step 8' or something. Of course it may depend on your computer too.


GC-Martijn(Posted 2003) [#5]
H! Todd i'm at home and testing things

but when I edit your code a bit the program don't work :(

Here is the code "I was only testing to select/deselect cone2"

Graphics3D 320,240,16,2
SetBuffer BackBuffer()

Global cursor
Global Cube1Sel=0

camera=CreateCamera() 
PositionEntity camera,0,0,-6

light=CreateLight() 
RotateEntity light,90,0,0 

;cursor
;cursor	= LoadImage("images/menu/cursor.png")
;MaskImage cursor,0,255,255

;pickable object 1
cone=CreateCone(20) 
EntityPickMode cone,2
PositionEntity cone,0,0,5 

;pickable object 2
cone2=CreateCone(5) 
EntityPickMode cone2,2
PositionEntity cone2,3,3,3 

While Not KeyHit(1)
Cls
	If Cube1Sel=1
		EntityColor cone2,0,255,0
	Else
		EntityColor cone2,255,0,0
	EndIf

UpdateWorld	 
RenderWorld

	If MouseDown(1)
		If mouseSet=0
			mouseSet=1
			mX=MouseX()
			mY=MouseY()
		EndIf
		Rect mX,mY,MouseX()-mX,MouseY()-mY,0
	Else
		If mouseSet=1
			mouseSet=0
			Cube1Sel=0
			Cube2Sel=0
			Cube3Sel=0
			For x=0 To MouseX()-mX Step 8
			For y=0 To MouseY()-mY Step 8
				Select CameraPick(camera,mX+x,mY+y)
				Case Cube1
					Cube1Sel=1
				Case Cube2
					Cube2Sel=1
				Case Cube3	
					Cube3Sel=1
				End Select
			Next
			Next
		EndIf
	EndIf

Flip
Wend



Todd(Posted 2003) [#6]
(edit) - ok, I just read the part that said "i was only testing selecting/deselecting cone2" :) In that case all you need to do is change the line "Case Cube1" to "Case cone2". it was still looking for Cube1, but you had renamed it to cone2. I'll leave all the rest of this just in case though



Basically what you need to do goes like this:

1: Decide if the user has dragged a rectagle
2: If he has, then decide what objects were inside that rectangle
3: If an object was inside the rectangle, the set it's variable to '1', if not then set it to '0'
4: for all of the objects whose variables are set to '1', do something to make it obvious it is selected (such as making it brighter, white, etc.)

Ok, so to do that, first off you should do the rectangle code. What you already had before for dragging a rectangle will work fine, except that we need to know when the user has let go of the mouse button. If he has, then we move on to step 2. To decide if he has, at least in my code, I used a varible (called 'mouseSet'). When the user pressed the button, it was set to 1. Then If the button was not pressed, but 'mouseSet' was equal to 1, then i know the user has realeased the button.

So then to decide if the entity was inside the rectangle, we use CameraPick, like you were in your origional code. So before we do anything we reset all of the objects variables to '0'. Then we go through each pixel inside the rect, and use CameraPick on that pixel to see if it is an pickable entity. Since CameraPick will return the entity that was picked I use Select/Case, (If..Endif will work too, just a little differently) if the picked entity was any of the entities i want to be able to select, then we set its varible to '1'. You really don't need to go through each pixel, you can skip most of them, but you lose accuracy. I did this by using 'Step 4', which skipped every 4 pixels.

Now in the main loop, we go through all of the variables for our objects. If it is '1', then we do something (maybe highlight it or something), or if it is '0' then we do something else (like darken it).

Ok, that's a lot of stuff there. I hope at least some of it is useful, and helps you out. If it's still not making sense, don't give up on it, keep trying stuff til you get it to work. Some of this kind of stuff might be a little hard to understand, especially if you're just starting out.


Todd(Posted 2003) [#7]
(sorry, double post)


GC-Martijn(Posted 2003) [#8]
Thanks for the good information you give me Todd.

I'm still learning now and this helps a lot.
I try to edit the code today.


poopla(Posted 2003) [#9]
Theres a MUCH faster way to do this that doesnt use CameraPick at all. You dont even have to check every pixel, the picking is instantaneous. Ill put you all in suspense as I devise the working(non buggy) algo for yall. Stumbled across this 3 minutes ago :).


poopla(Posted 2003) [#10]
Got it! now to write a more general implementation of the techniqu and plop it in the archives.

[Edit] I dont have time to write that function right now, essentially you use the CameraProject() function on the 3d coods of each entity your trying to pick. Then PrejectedX() and ProjectedY() to find the 2d on screen coordinates, then you check the 2d coordinates to see if they are within your selection rect. If they are, the entity is picked, if not, it isnt picked. Pretty simple really, but FAST. Though it wont be pixel perfect, and will be really innacurate on large large entitys, you can use this, and a combo of "low-res" camerapick's to get the job done. Just remember to NOT camerapick() every pixel in the selection rectangle. That is really slow. Have fun.