Object Selection Using DirectX

Started by
18 comments, last by Dakiar 18 years, 10 months ago
Hidiho, don't have any idea how to implement object selection using directx. I've tried to find some ressources about that theme (goooooogle, rofl) but ... bad luck! Maybe some of you guys can help me? (would be nice if you just could show me some simple code for better understanding)
Advertisement
Quote:Original post by codex01
Hidiho,

don't have any idea how to implement object selection using directx. I've tried to find some ressources about that theme (goooooogle, rofl) but ... bad luck!

Maybe some of you guys can help me?
(would be nice if you just could show me some simple code for better understanding)




There is a sample program in the directx SDK called 'pick' that shows how to select individual triangles on a mesh (using a mouse to poibt to it).

I dont know if there are any higher fuunctions (display list oriented stuff), but using the same method and traversing your object hierarchy yourself, you should be able to do what you need.

Hi,

damn ... "pick" ... I've tried "selecting objects", "object selection" ... but "PICK" ... well, maybe I should improve my english ;) ROFL.

Thx a lot that will help me

Greetz
Quote:Original post by codex01
damn ... "pick" ... I've tried "selecting objects", "object selection" ... but "PICK" ... well, maybe I should improve my english ;) ROFL.

Nope. "Object Selection" is much better english than "pick". Pick is programmer slang for the specific situation of finding the exact polygons the mouse is over. You may be able to select objects in an easier way depending on your game. Bounding boxes or such.
Hell, I'm on it ... trying to implement it ... arrrgh ... on success I'll post my results.

Seems to me that a lot more people have this problem. Google gives me only forums with the same question but without any solution!

Is this a lack of dx? OpenGl has a quite easy solution for this problem!

arrrgh


I know what you mean. I'm setting up a game map of irregular bounded regions, which I would like to be able to select and match to an assigned territory name in game play.
It seems odd that ExtFloodFill can fill an irregular bounded region with a certain color, but no function exists to determine a bounded region as a set of points. This would take a lot of coding on my own and who knows how much memory.
I would be very interested to know how your progress is coming along and what you have tried so far. I'm considering that multiple rectangle approximation (bounding boxes) suggestion.
Hi,

we are using a simple method here:
we have a spearate render target called "pick screen", to which we render the same scene as to te screen but using the id of objects as colors (we have 32 bits in RGBA which is enough even for coloring every face differently).
You have to play with the light and with vertex colors, but it works well.
Yes, you have to lock this rendertarget if you want to read the pixel under the mouse, but it's not that big problem.

kp
------------------------------------------------------------Neo, the Matrix should be 16-byte aligned for better performance!
Hi there again,

well, finally I figured out two methods for picking. The first one is faster but the second one is better ROFL.
Both samples are in C#, but also also portable to c++ without any problems.
Using c++ you have to use the D3DX libs for some calculations, but I'm sure you guys know how to do this.

Here's the first one:

First we ne a struct, which holds out picking ray:

public struct PickingRay
{
public Vector3 origin;
public Vector3 direction;
};


If the user now clicks on the viewport we take the mouse x and y positions and pass it over to this function (m_RenderTarget in this case is a simple picturebox control.)

public PickingRay CalculatePickingRay(int x, int y)
{
Matrix view, viewInverse;
PickingRay ray;
float px = 0.0f;
float py = 0.0f;

px = ((( 2.0f*x) / m_RenderTarget.Width) - 1.0f) / d3dDevice.Transform.Projection.M11;

py = (((-2.0f*y) / m_RenderTarget.Height) + 1.0f) / d3dDevice.Transform.Projection.M22;

ray.origin = new Vector3(0.0f, 0.0f, 0.0f);
ray.direction = new Vector3(px, py, 1.0f);

view = d3dDevice.GetTransform(TransformType.View);
viewInverse = Matrix.Invert(view);

ray.origin = Vector3.TransformCoordinate(ray.origin, viewInverse);
ray.direction = Vector3.TransformNormal(ray.direction, viewInverse);
ray.direction.Normalize();

return ray;
}

To get use of the ray we get from the function above we need another function that calculates if the ray hits a boundingsphere of an object. For calculation a boundingsphere of a model you have free hands, it's quite simple and there a lot of tutorials on the web.
BUT in fact you will need a struct that holds the required data ... it could look like this:

public struct BoundingSphere
{
public Microsoft.DirectX.Vector3 center;
public float radius;
};

And now here's the function which tells us, if we have hit an object:

public bool RaySphereIntersectionTest(HotRod.PickingRay ray, HotRod.BoundingSphere bs)
{
Vector3 v = ray.origin - bs.center;

float b = 2.0f * Vector3.Dot(ray.direction, v);
float c = Vector3.Dot(v, v) - bs.radius * bs.radius;

float discriminant = (b * b) - (4.0f * c);

if( discriminant < 0.0f )
return false;

discriminant = (float) System.Math.Sqrt(discriminant);

float s0 = (-b + discriminant) / 2.0f;
float s1 = (-b - discriminant) / 2.0f;

if( s0 >= 0.0f || s1 >= 0.0f )
return true;
return false;
}


And that's it. In this case we just decide if an object was hit or not (returns a boolean value). Of course you have to search your objects hirarchy to find the propper object and so on. But example ist a good start to get deeper into this subject.

The next one I will post seperately.
Ok, heres the next example.

Well, in fact this one is more exact (pixelwise, 100%) but of course it takes more time which means that your game will slow donw a bit.
BUT ... this one also can be implemented more effective.

But first let's have a look at that sample:

Image you have a class with name ENTITY, which represents a star destroyer or something like this.
Every entity has a world matrix and a model to which it points.
The model holds a simple DirectX Mesh which has a very nice method called Intersect(...).
And this method we are going to use.
Also, all of our entities are collected in a so called RESOURCEMANAGER.
Imagine this as a simple HashTable.

And here we go:
First of all our function takes the mouse x and y screencoordinates and returns an object of type ENTITY that has been picked by the mouse.


public Entity PickEntity(int x, int y)
{
Microsoft.DirectX.Vector3 vNear = new Microsoft.DirectX.Vector3();
Microsoft.DirectX.Vector3 vFar = new Microsoft.DirectX.Vector3();

System.Collections.IDictionaryEnumerator en = RessourceManager.m_Entities.GetEnumerator();

while (en.MoveNext())
{
vNear.X = (float)x;
vNear.Y = (float)y;
vNear.Z = 0.0f;
vFar.X = (float)x;
vFar.Y = (float)y;
vFar.Z = 1.0f;

Entity entity = (Entity) en.Value;

vNear.Unproject(d3dDevice.Viewport,
d3dDevice.Transform.Projection,
d3dDevice.Transform.View,
entity.World);
vFar.Unproject(d3dDevice.Viewport,
d3dDevice.Transform.Projection,
d3dDevice.Transform.View,
entity.World);

vFar.Subtract(vNear);
vFar.Normalize();

if (entity.Model.m_Mesh.Intersect(vNear, vFar))
return entity;
}
return null;
}


REMARKS:
It is very important, that if you change the position of your objects you have to do this by the objects world matrix!!! otherwise the picking will not work.
Also important is the projection together with the viewport matrix.

Ok guys, have fun.
If there are any questions .. ask ROFL

greetz
I've been working with the following idea:

createPolygonRgn();

if(PtInRegion())

as the following illustrates...

case WM_LBUTTONUP:
{
//borrow dc from main window
HDC hdc=GetDC(hWndMain);
char buffer[80];

// extract x,y and buttons

//extract x and y from lparam
int mouse_x = LOWORD(lParam);
int mouse_y = HIWORD(lParam);

if(PtInRegion(hrgn_IKI_0,mouse_x,mouse_y))
{
sprintf(buffer,"Yes, x=%d y=%d is in Iki (hrgn_IKI_0) ",
mouse_x, mouse_y);

// set the colors
SetTextColor(hdc, RGB(0,0,255));
SetBkColor(hdc,RGB(0,0,0));
SetBkMode(hdc, OPAQUE);

// output the message
TextOut(hdc,0,660,buffer,strlen(buffer));
}
//return the borrowed dc to the system
ReleaseDC(hWndMain,hdc);
}

This topic is closed to new replies.

Advertisement